Coverage for python/lsst/daf/butler/core/dimensions/construction.py: 51%
52 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-08-12 09:20 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-08-12 09:20 +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
24from abc import ABC, abstractmethod
25from collections.abc import Iterable, Set
26from typing import TYPE_CHECKING
28from .._topology import TopologicalFamily, TopologicalSpace
29from ..named import NamedValueSet
31if TYPE_CHECKING:
32 from ._config import DimensionConfig
33 from ._elements import Dimension, DimensionElement
34 from ._packer import DimensionPackerFactory
37class DimensionConstructionVisitor(ABC):
38 """For adding entities to a builder class.
40 An abstract base class for adding one or more entities to a
41 `DimensionConstructionBuilder`.
43 Parameters
44 ----------
45 name : `str`
46 Name of an entity being added. This must be unique across all
47 entities, which include `DimensionElement`, `TopologicalFamily`, and
48 `DimensionPackerFactory` objects. The visitor may add other entities
49 as well, as long as only the named entity is referenced by other
50 entities in the universe.
51 """
53 def __init__(self, name: str):
54 self.name = name
56 def __str__(self) -> str:
57 return self.name
59 @abstractmethod
60 def hasDependenciesIn(self, others: Set[str]) -> bool:
61 """Test if dependencies have already been constructed.
63 Tests whether other entities this visitor depends on have already
64 been constructed.
66 Parameters
67 ----------
68 others : `~collections.abc.Set` [ `str` ]
69 The names of other visitors that have not yet been invoked.
71 Returns
72 -------
73 blocked : `bool`
74 If `True`, this visitor has dependencies on other visitors that
75 must be invoked before this one can be. If `False`, `visit` may
76 be called.
77 """
78 raise NotImplementedError()
80 @abstractmethod
81 def visit(self, builder: DimensionConstructionBuilder) -> None:
82 """Modify the given builder object to include responsible entities.
84 Parameters
85 ----------
86 builder : `DimensionConstructionBuilder`
87 Builder to modify in-place and from which dependencies can be
88 obtained.
90 Notes
91 -----
92 Subclasses may assume (and callers must guarantee) that
93 `hasDependenciesIn` would return `False` prior to `visit` being
94 called.
95 """
96 raise NotImplementedError()
99class DimensionConstructionBuilder:
100 """A builder object for constructing `DimensionUniverse` instances.
102 `DimensionConstructionVisitor` objects can be added to a
103 `DimensionConstructionBuilder` object in any order, and are invoked
104 in a deterministic order consistent with their dependency relationships
105 by a single call (by the `DimensionUniverse`) to the `finish` method.
107 Parameters
108 ----------
109 version : `int`
110 Version for the `DimensionUniverse`.
111 commonSkyPixName : `str`
112 Name of the "common" skypix dimension that is used to relate all other
113 spatial `TopologicalRelationshipEndpoint` objects.
114 namespace : `str`, optional
115 The namespace to assign to this universe.
116 visitors : `~collections.abc.Iterable` [ `DimensionConstructionVisitor` ]
117 Visitor instances to include from the start.
118 """
120 def __init__(
121 self,
122 version: int,
123 commonSkyPixName: str,
124 config: DimensionConfig,
125 *,
126 namespace: str | None = None,
127 visitors: Iterable[DimensionConstructionVisitor] = (),
128 ) -> None:
129 self.dimensions = NamedValueSet()
130 self.elements = NamedValueSet()
131 self.topology = {space: NamedValueSet() for space in TopologicalSpace.__members__.values()}
132 self.packers = {}
133 self.version = version
134 self.namespace = namespace
135 self.config = config
136 self.commonSkyPixName = commonSkyPixName
137 self._todo: dict[str, DimensionConstructionVisitor] = {v.name: v for v in visitors}
139 def add(self, visitor: DimensionConstructionVisitor) -> None:
140 """Add a single visitor to the builder.
142 Parameters
143 ----------
144 visitor : `DimensionConstructionVisitor`
145 Visitor instance to add.
146 """
147 self._todo[visitor.name] = visitor
149 def update(self, visitors: Iterable[DimensionConstructionVisitor]) -> None:
150 """Add multiple visitors to the builder.
152 Parameters
153 ----------
154 visitors : `~collections.abc.Iterable` \
155 [ `DimensionConstructionVisitor` ]
156 Visitor instances to add.
157 """
158 self._todo.update((v.name, v) for v in visitors)
160 def finish(self) -> None:
161 """Complete construction of the builder.
163 This method invokes all visitors in an order consistent with their
164 dependencies, fully populating all public attributes. It should be
165 called only once, by `DimensionUniverse` itself.
166 """
167 while self._todo:
168 unblocked = [
169 name
170 for name, visitor in self._todo.items()
171 if not visitor.hasDependenciesIn(self._todo.keys())
172 ]
173 unblocked.sort() # Break ties lexicographically.
174 if not unblocked:
175 raise RuntimeError(f"Cycle or unmet dependency in dimension elements: {self._todo.keys()}.")
176 for name in unblocked:
177 self._todo.pop(name).visit(self)
179 version: int
180 """Version number for the `DimensionUniverse` (`int`).
182 Populated at builder construction.
183 """
185 namespace: str | None
186 """Namespace for the `DimensionUniverse` (`str`)
188 Populated at builder construction.
189 """
191 commonSkyPixName: str
192 """Name of the common skypix dimension used to connect other spatial
193 `TopologicalRelationshipEndpoint` objects (`str`).
195 Populated at builder construction.
196 """
198 dimensions: NamedValueSet[Dimension]
199 """Set of all `Dimension` objects (`NamedValueSet` [ `Dimension` ]).
201 Populated by `finish`.
202 """
204 elements: NamedValueSet[DimensionElement]
205 """Set of all `DimensionElement` objects
206 (`NamedValueSet` [ `DimensionElement` ]).
208 Populated by `finish`. `DimensionConstructionVisitor` classes that
209 construct `Dimension` objects are responsible for adding them to this
210 set as well as `dimensions`.
211 """
213 topology: dict[TopologicalSpace, NamedValueSet[TopologicalFamily]]
214 """Dictionary containing all `TopologicalFamily` objects
215 (`dict` [ `TopologicalSpace`, `NamedValueSet` [ `TopologicalFamily` ] ] ).
216 """
218 packers: dict[str, DimensionPackerFactory]
219 """Dictionary containing all `DimensionPackerFactory` objects
220 (`dict` [ `str`, `DimensionPackerFactory` ] ).
221 """