Coverage for python/lsst/daf/butler/core/dimensions/construction.py: 45%
57 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-22 03:05 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-22 03:05 -0800
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 typing import TYPE_CHECKING, AbstractSet, Dict, Iterable, Optional
27from .._topology import TopologicalFamily, TopologicalSpace
28from ..named import NamedValueSet
30if TYPE_CHECKING: 30 ↛ 31line 30 didn't jump to line 31, because the condition on line 30 was never true
31 from ._config import DimensionConfig
32 from ._elements import Dimension, DimensionElement
33 from ._packer import DimensionPackerFactory
36class DimensionConstructionVisitor(ABC):
37 """For adding entities to a builder class.
39 An abstract base class for adding one or more entities to a
40 `DimensionConstructionBuilder`.
42 Parameters
43 ----------
44 name : `str`
45 Name of an entity being added. This must be unique across all
46 entities, which include `DimensionElement`, `TopologicalFamily`, and
47 `DimensionPackerFactory` objects. The visitor may add other entities
48 as well, as long as only the named entity is referenced by other
49 entities in the universe.
50 """
52 def __init__(self, name: str):
53 self.name = name
55 def __str__(self) -> str:
56 return self.name
58 @abstractmethod
59 def hasDependenciesIn(self, others: AbstractSet[str]) -> bool:
60 """Test if dependencies have already been constructed.
62 Tests whether other entities this visitor depends on have already
63 been constructed.
65 Parameters
66 ----------
67 others : `AbstractSet` [ `str` ]
68 The names of other visitors that have not yet been invoked.
70 Returns
71 -------
72 blocked : `bool`
73 If `True`, this visitor has dependencies on other visitors that
74 must be invoked before this one can be. If `False`, `visit` may
75 be called.
76 """
77 raise NotImplementedError()
79 @abstractmethod
80 def visit(self, builder: DimensionConstructionBuilder) -> None:
81 """Modify the given builder object to include responsible entities.
83 Parameters
84 ----------
85 builder : `DimensionConstructionBuilder`
86 Builder to modify in-place and from which dependencies can be
87 obtained.
89 Notes
90 -----
91 Subclasses may assume (and callers must guarantee) that
92 `hasDependenciesIn` would return `False` prior to `visit` being
93 called.
94 """
95 raise NotImplementedError()
98class DimensionConstructionBuilder:
99 """A builder object for constructing `DimensionUniverse` instances.
101 `DimensionConstructionVisitor` objects can be added to a
102 `DimensionConstructionBuilder` object in any order, and are invoked
103 in a deterministic order consistent with their dependency relationships
104 by a single call (by the `DimensionUniverse`) to the `finish` method.
106 Parameters
107 ----------
108 version : `int`
109 Version for the `DimensionUniverse`.
110 commonSkyPixName : `str`
111 Name of the "common" skypix dimension that is used to relate all other
112 spatial `TopologicalRelationshipEndpoint` objects.
113 namespace : `str`, optional
114 The namespace to assign to this universe.
115 visitors : `Iterable` [ `DimensionConstructionVisitor` ]
116 Visitor instances to include from the start.
117 """
119 def __init__(
120 self,
121 version: int,
122 commonSkyPixName: str,
123 config: DimensionConfig,
124 *,
125 namespace: Optional[str] = None,
126 visitors: Iterable[DimensionConstructionVisitor] = (),
127 ) -> None:
128 self.dimensions = NamedValueSet()
129 self.elements = NamedValueSet()
130 self.topology = {space: NamedValueSet() for space in TopologicalSpace.__members__.values()}
131 self.packers = {}
132 self.version = version
133 self.namespace = namespace
134 self.config = config
135 self.commonSkyPixName = commonSkyPixName
136 self._todo: Dict[str, DimensionConstructionVisitor] = {v.name: v for v in visitors}
138 def add(self, visitor: DimensionConstructionVisitor) -> None:
139 """Add a single visitor to the builder.
141 Parameters
142 ----------
143 visitor : `DimensionConstructionVisitor`
144 Visitor instance to add.
145 """
146 self._todo[visitor.name] = visitor
148 def update(self, visitors: Iterable[DimensionConstructionVisitor]) -> None:
149 """Add multiple visitors to the builder.
151 Parameters
152 ----------
153 visitors : `Iterable` [ `DimensionConstructionVisitor` ]
154 Visitor instances to add.
155 """
156 self._todo.update((v.name, v) for v in visitors)
158 def finish(self) -> None:
159 """Complete construction of the builder.
161 This method invokes all visitors in an order consistent with their
162 dependencies, fully populating all public attributes. It should be
163 called only once, by `DimensionUniverse` itself.
164 """
165 while self._todo:
166 unblocked = [
167 name
168 for name, visitor in self._todo.items()
169 if not visitor.hasDependenciesIn(self._todo.keys())
170 ]
171 unblocked.sort() # Break ties lexicographically.
172 if not unblocked:
173 raise RuntimeError(f"Cycle or unmet dependency in dimension elements: {self._todo.keys()}.")
174 for name in unblocked:
175 self._todo.pop(name).visit(self)
177 version: int
178 """Version number for the `DimensionUniverse` (`int`).
180 Populated at builder construction.
181 """
183 namespace: Optional[str]
184 """Namespace for the `DimensionUniverse` (`str`)
186 Populated at builder construction.
187 """
189 commonSkyPixName: str
190 """Name of the common skypix dimension used to connect other spatial
191 `TopologicalRelationshipEndpoint` objects (`str`).
193 Populated at builder construction.
194 """
196 dimensions: NamedValueSet[Dimension]
197 """Set of all `Dimension` objects (`NamedValueSet` [ `Dimension` ]).
199 Populated by `finish`.
200 """
202 elements: NamedValueSet[DimensionElement]
203 """Set of all `DimensionElement` objects
204 (`NamedValueSet` [ `DimensionElement` ]).
206 Populated by `finish`. `DimensionConstructionVisitor` classes that
207 construct `Dimension` objects are responsible for adding them to this
208 set as well as `dimensions`.
209 """
211 topology: Dict[TopologicalSpace, NamedValueSet[TopologicalFamily]]
212 """Dictionary containing all `TopologicalFamily` objects
213 (`dict` [ `TopologicalSpace`, `NamedValueSet` [ `TopologicalFamily` ] ] ).
214 """
216 packers: Dict[str, DimensionPackerFactory]
217 """Dictionary containing all `DimensionPackerFactory` objects
218 (`dict` [ `str`, `DimensionPackerFactory` ] ).
219 """