Coverage for python/lsst/daf/butler/core/dimensions/_governor.py: 49%
59 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-12-01 19:55 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2022-12-01 19:55 +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 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/>.
22from __future__ import annotations
24__all__ = (
25 "GovernorDimension",
26)
28from types import MappingProxyType
29from typing import (
30 AbstractSet,
31 Iterable,
32 Mapping,
33 Optional,
34 TYPE_CHECKING,
35)
37from lsst.utils import doImport
39from .. import ddl
40from ..named import NamedValueAbstractSet, NamedValueSet
41from .._topology import TopologicalFamily, TopologicalSpace
43from ._elements import Dimension
44from .construction import DimensionConstructionBuilder, DimensionConstructionVisitor
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 )
54class GovernorDimension(Dimension):
55 """Governor dimension.
57 A special `Dimension` with no dependencies and a small number of rows,
58 used to group the dimensions that depend on it.
60 Parameters
61 ----------
62 name : `str`
63 Name of the dimension.
64 storage : `dict`
65 Fully qualified name of the `GovernorDimensionRecordStorage` subclass
66 that will back this element in the registry (in a "cls" key) along
67 with any other construction keyword arguments (in other keys).
68 metadata : `NamedValueAbstractSet` [ `ddl.FieldSpec` ]
69 Field specifications for all non-key fields in this dimension's table.
70 uniqueKeys : `NamedValueAbstractSet` [ `ddl.FieldSpec` ]
71 Fields that can each be used to uniquely identify this dimension (given
72 values for all required dimensions). The first of these is used as
73 (part of) this dimension's table's primary key, while others are used
74 to define unique constraints.
76 Notes
77 -----
78 Most dimensions have exactly one governor dimension as a required
79 dependency, and queries that involve those dimensions are always expected
80 to explicitly identify the governor dimension value(s), rather than
81 retrieve all matches from the database. Because governor values are thus
82 almost always known at query-generation time, they can be used there to
83 simplify queries, provide sensible defaults, or check in advance for common
84 mistakes that might otherwise yield confusing (albeit formally correct)
85 results instead of straightforward error messages.
87 Governor dimensions may not be associated with any kind of topological
88 extent.
90 Governor dimension rows are often affiliated with a Python class or
91 instance (e.g. `lsst.obs.base.Instrument`) that is capable of generating
92 the rows of at least some dependent dimensions or providing other related
93 functionality. In the future, we hope to attach these instances to
94 governor dimension records (instantiating them from information in the
95 database row when it is fetched), and use those objects to add additional
96 functionality to governor dimensions, but a number of (code) dependency
97 relationships would need to be reordered first.
98 """
100 def __init__(
101 self,
102 name: str,
103 storage: dict, *,
104 metadata: NamedValueAbstractSet[ddl.FieldSpec],
105 uniqueKeys: NamedValueAbstractSet[ddl.FieldSpec],
106 ):
107 self._name = name
108 self._storage = storage
109 self._required = NamedValueSet({self}).freeze()
110 self._metadata = metadata
111 self._uniqueKeys = uniqueKeys
112 if self.primaryKey.getPythonType() is not str:
113 raise TypeError(f"Governor dimension '{name}' must have a string primary key (configured type "
114 f"is {self.primaryKey.dtype.__name__}).")
115 if self.primaryKey.length is not None and self.primaryKey.length > self.MAX_KEY_LENGTH:
116 raise TypeError(f"Governor dimension '{name}' must have a string primary key with length <= "
117 f"{self.MAX_KEY_LENGTH} (configured value is {self.primaryKey.length}).")
119 MAX_KEY_LENGTH = 128
121 @property
122 def name(self) -> str:
123 # Docstring inherited from TopoogicalRelationshipEndpoint.
124 return self._name
126 @property
127 def required(self) -> NamedValueAbstractSet[Dimension]:
128 # Docstring inherited from DimensionElement.
129 return self._required
131 @property
132 def implied(self) -> NamedValueAbstractSet[Dimension]:
133 # Docstring inherited from DimensionElement.
134 return NamedValueSet().freeze()
136 @property
137 def topology(self) -> Mapping[TopologicalSpace, TopologicalFamily]:
138 # Docstring inherited from TopologicalRelationshipEndpoint
139 return MappingProxyType({})
141 @property
142 def metadata(self) -> NamedValueAbstractSet[ddl.FieldSpec]:
143 # Docstring inherited from DimensionElement.
144 return self._metadata
146 @property
147 def uniqueKeys(self) -> NamedValueAbstractSet[ddl.FieldSpec]:
148 # Docstring inherited from Dimension.
149 return self._uniqueKeys
151 def makeStorage(
152 self,
153 db: Database, *,
154 context: Optional[StaticTablesContext] = None,
155 ) -> GovernorDimensionRecordStorage:
156 """Make storage record.
158 Constructs the `DimensionRecordStorage` instance that should
159 be used to back this element in a registry.
161 Parameters
162 ----------
163 db : `Database`
164 Interface to the underlying database engine and namespace.
165 context : `StaticTablesContext`, optional
166 If provided, an object to use to create any new tables. If not
167 provided, ``db.ensureTableExists`` should be used instead.
169 Returns
170 -------
171 storage : `GovernorDimensionRecordStorage`
172 Storage object that should back this element in a registry.
173 """
174 from ...registry.interfaces import GovernorDimensionRecordStorage
175 cls = doImport(self._storage["cls"])
176 assert issubclass(cls, GovernorDimensionRecordStorage)
177 return cls.initialize(db, self, context=context, config=self._storage)
180class GovernorDimensionConstructionVisitor(DimensionConstructionVisitor):
181 """A construction visitor for `GovernorDimension`.
183 Parameters
184 ----------
185 name : `str`
186 Name of the dimension.
187 storage : `dict`
188 Fully qualified name of the `GovernorDimensionRecordStorage` subclass
189 that will back this element in the registry (in a "cls" key) along
190 with any other construction keyword arguments (in other keys).
191 metadata : `Iterable` [ `ddl.FieldSpec` ]
192 Field specifications for all non-key fields in this element's table.
193 uniqueKeys : `Iterable` [ `ddl.FieldSpec` ]
194 Fields that can each be used to uniquely identify this dimension (given
195 values for all required dimensions). The first of these is used as
196 (part of) this dimension's table's primary key, while others are used
197 to define unique constraints.
198 """
200 def __init__(
201 self,
202 name: str,
203 storage: dict, *,
204 metadata: Iterable[ddl.FieldSpec] = (),
205 uniqueKeys: Iterable[ddl.FieldSpec] = (),
206 ):
207 super().__init__(name)
208 self._storage = storage
209 self._metadata = NamedValueSet(metadata).freeze()
210 self._uniqueKeys = NamedValueSet(uniqueKeys).freeze()
212 def hasDependenciesIn(self, others: AbstractSet[str]) -> bool:
213 # Docstring inherited from DimensionConstructionVisitor.
214 return False
216 def visit(self, builder: DimensionConstructionBuilder) -> None:
217 # Docstring inherited from DimensionConstructionVisitor.
218 # Special handling for creating Dimension instances.
219 dimension = GovernorDimension(
220 self.name,
221 storage=self._storage,
222 metadata=self._metadata,
223 uniqueKeys=self._uniqueKeys,
224 )
225 builder.dimensions.add(dimension)
226 builder.elements.add(dimension)