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
« 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
23__all__ = ("QueryBackend",)
25from abc import ABC, abstractmethod
26from collections.abc import Set
27from typing import TYPE_CHECKING, Any
29from .._collectionType import CollectionType
30from .._exceptions import DatasetTypeError, MissingDatasetTypeError
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
38class QueryBackend(ABC):
39 """An interface for constructing and evaluating the
40 `~lsst.daf.relation.Relation` objects that comprise registry queries.
42 This ABC is expected to have a concrete subclass for each concrete registry
43 type.
44 """
46 @property
47 @abstractmethod
48 def managers(self) -> RegistryManagerInstances:
49 """A struct containing the manager instances that back a SQL registry.
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()
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()
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.
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.
98 Returns
99 ------
100 records : `list` [ `CollectionRecord` ]
101 Matching collection records.
102 """
103 raise NotImplementedError()
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.
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.
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()
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.
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.
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.
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