Coverage for python/lsst/daf/butler/registry/queries/_query_backend.py: 40%

39 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-10-07 02:47 -0700

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

21from __future__ import annotations 

22 

23__all__ = ("QueryBackend",) 

24 

25from abc import ABC, abstractmethod 

26from collections.abc import Set 

27from typing import TYPE_CHECKING, Any 

28 

29from .._collectionType import CollectionType 

30from .._exceptions import DatasetTypeError, MissingDatasetTypeError 

31 

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

33 from ...core import DatasetType, DimensionUniverse 

34 from ..interfaces import CollectionRecord 

35 from ..managers import RegistryManagerInstances 

36 

37 

38class QueryBackend(ABC): 

39 """An interface for constructing and evaluating the 

40 `~lsst.daf.relation.Relation` objects that comprise registry queries. 

41 

42 This ABC is expected to have a concrete subclass for each concrete registry 

43 type. 

44 """ 

45 

46 @property 

47 @abstractmethod 

48 def managers(self) -> RegistryManagerInstances: 

49 """A struct containing the manager instances that back a SQL registry. 

50 

51 Notes 

52 ----- 

53 This property is a temporary interface that will be removed in favor of 

54 new methods once the manager and storage classes have been integrated 

55 with `~lsst.daf.relation.Relation`. 

56 """ 

57 raise NotImplementedError() 

58 

59 @property 

60 @abstractmethod 

61 def universe(self) -> DimensionUniverse: 

62 """Definition of all dimensions and dimension elements for this 

63 registry. 

64 """ 

65 raise NotImplementedError() 

66 

67 @abstractmethod 

68 def resolve_collection_wildcard( 

69 self, 

70 expression: Any, 

71 *, 

72 collection_types: Set[CollectionType] = CollectionType.all(), 

73 done: set[str] | None = None, 

74 flatten_chains: bool = True, 

75 include_chains: bool | None = None, 

76 ) -> list[CollectionRecord]: 

77 """Return the collection records that match a wildcard expression. 

78 

79 Parameters 

80 ---------- 

81 expression 

82 Names and/or patterns for collections; will be passed to 

83 `CollectionWildcard.from_expression`. 

84 collection_types : `collections.abc.Set` [ `CollectionType` ], optional 

85 If provided, only yield collections of these types. 

86 done : `set` [ `str` ], optional 

87 A set of collection names that should be skipped, updated to 

88 include all processed collection names on return. 

89 flatten_chains : `bool`, optional 

90 If `True` (default) recursively yield the child collections of 

91 `~CollectionType.CHAINED` collections. 

92 include_chains : `bool`, optional 

93 If `False`, return records for `~CollectionType.CHAINED` 

94 collections themselves. The default is the opposite of 

95 ``flattenChains``: either return records for CHAINED collections or 

96 their children, but not both. 

97 

98 Returns 

99 ------ 

100 records : `list` [ `CollectionRecord` ] 

101 Matching collection records. 

102 """ 

103 raise NotImplementedError() 

104 

105 @abstractmethod 

106 def resolve_dataset_type_wildcard( 

107 self, 

108 expression: Any, 

109 components: bool | None = None, 

110 missing: list[str] | None = None, 

111 explicit_only: bool = False, 

112 ) -> dict[DatasetType, list[str | None]]: 

113 """Return the dataset types that match a wildcard expression. 

114 

115 Parameters 

116 ---------- 

117 expression 

118 Names and/or patterns for dataset types; will be passed to 

119 `DatasetTypeWildcard.from_expression`. 

120 components : `bool`, optional 

121 If `True`, apply all expression patterns to component dataset type 

122 names as well. If `False`, never apply patterns to components. If 

123 `None` (default), apply patterns to components only if their parent 

124 datasets were not matched by the expression. Fully-specified 

125 component datasets (`str` or `DatasetType` instances) are always 

126 included. 

127 missing : `list` of `str`, optional 

128 String dataset type names that were explicitly given (i.e. not 

129 regular expression patterns) but not found will be appended to this 

130 list, if it is provided. 

131 explicit_only : `bool`, optional 

132 If `True`, require explicit `DatasetType` instances or `str` names, 

133 with `re.Pattern` instances deprecated and ``...`` prohibited. 

134 

135 Returns 

136 ------- 

137 dataset_types : `dict` [ `DatasetType`, `list` [ `None`, `str` ] ] 

138 A mapping with resolved dataset types as keys and lists of 

139 matched component names as values, where `None` indicates the 

140 parent composite dataset type was matched. 

141 """ 

142 raise NotImplementedError() 

143 

144 def resolve_single_dataset_type_wildcard( 

145 self, 

146 expression: Any, 

147 components: bool | None = None, 

148 explicit_only: bool = False, 

149 ) -> tuple[DatasetType, list[str | None]]: 

150 """Return a single dataset type that matches a wildcard expression. 

151 

152 Parameters 

153 ---------- 

154 expression 

155 Names and/or patterns for the dataset type; will be passed to 

156 `DatasetTypeWildcard.from_expression`. 

157 components : `bool`, optional 

158 If `True`, apply all expression patterns to component dataset type 

159 names as well. If `False`, never apply patterns to components. If 

160 `None` (default), apply patterns to components only if their parent 

161 datasets were not matched by the expression. Fully-specified 

162 component datasets (`str` or `DatasetType` instances) are always 

163 included. 

164 

165 Values other than `False` are deprecated, and only `False` will be 

166 supported after v26. After v27 this argument will be removed 

167 entirely. 

168 explicit_only : `bool`, optional 

169 If `True`, require explicit `DatasetType` instances or `str` names, 

170 with `re.Pattern` instances deprecated and ``...`` prohibited. 

171 

172 Returns 

173 ------- 

174 single_parent : `DatasetType` 

175 The matched parent dataset type. 

176 single_components : `list` [ `str` | `None` ] 

177 The matched components that correspond to this parent, or `None` if 

178 the parent dataset type itself was matched. 

179 

180 Notes 

181 ----- 

182 This method really finds a single parent dataset type and any number of 

183 components, because it's only the parent dataset type that's known to 

184 registry at all; many callers are expected to discard the 

185 ``single_components`` return value. 

186 """ 

187 missing: list[str] = [] 

188 matching = self.resolve_dataset_type_wildcard( 

189 expression, components=components, missing=missing, explicit_only=explicit_only 

190 ) 

191 if not matching: 

192 if missing: 

193 raise MissingDatasetTypeError( 

194 "\n".join( 

195 f"Dataset type {t!r} is not registered, so no instances of it can exist." 

196 for t in missing 

197 ) 

198 ) 

199 else: 

200 raise MissingDatasetTypeError( 

201 f"No registered dataset types matched expression {expression!r}, " 

202 "so no datasets will be found." 

203 ) 

204 if len(matching) > 1: 

205 raise DatasetTypeError( 

206 f"Expression {expression!r} matched multiple parent dataset types: " 

207 f"{[t.name for t in matching]}, but only one is allowed." 

208 ) 

209 ((single_parent, single_components),) = matching.items() 

210 if missing: 

211 raise DatasetTypeError( 

212 f"Expression {expression!r} appears to involve multiple dataset types, even though only " 

213 f"one ({single_parent.name}) is registered, and only one is allowed here." 

214 ) 

215 return single_parent, single_components