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-04 02:19 -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 explicit_only : `bool`, optional 

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

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

167 

168 Returns 

169 ------- 

170 single_parent : `DatasetType` 

171 The matched parent dataset type. 

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

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

174 the parent dataset type itself was matched. 

175 

176 Notes 

177 ----- 

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

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

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

181 ``single_components`` return value. 

182 """ 

183 missing: list[str] = [] 

184 matching = self.resolve_dataset_type_wildcard( 

185 expression, components=components, missing=missing, explicit_only=explicit_only 

186 ) 

187 if not matching: 

188 if missing: 

189 raise MissingDatasetTypeError( 

190 "\n".join( 

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

192 for t in missing 

193 ) 

194 ) 

195 else: 

196 raise MissingDatasetTypeError( 

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

198 "so no datasets will be found." 

199 ) 

200 if len(matching) > 1: 

201 raise DatasetTypeError( 

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

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

204 ) 

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

206 if missing: 

207 raise DatasetTypeError( 

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

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

210 ) 

211 return single_parent, single_components