Coverage for python/lsst/daf/butler/dimensions/construction.py: 54%
43 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-08 02:51 -0700
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-08 02:51 -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 software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
28from __future__ import annotations
30from abc import ABC, abstractmethod
31from collections.abc import Set
32from typing import TYPE_CHECKING
34from .._named import NamedValueSet
35from .._topology import TopologicalFamily, TopologicalSpace
37if TYPE_CHECKING:
38 from ._config import DimensionConfig
39 from ._elements import Dimension, DimensionElement
42class DimensionConstructionVisitor(ABC):
43 """For adding entities to a builder class.
45 An abstract base class for adding one or more entities to a
46 `DimensionConstructionBuilder`.
47 """
49 @abstractmethod
50 def has_dependencies_in(self, others: Set[str]) -> bool:
51 """Test if dependencies have already been constructed.
53 Tests whether other entities this visitor depends on have already
54 been constructed.
56 Parameters
57 ----------
58 others : `~collections.abc.Set` [ `str` ]
59 The names of other visitors that have not yet been invoked.
61 Returns
62 -------
63 blocked : `bool`
64 If `True`, this visitor has dependencies on other visitors that
65 must be invoked before this one can be. If `False`, `visit` may
66 be called.
67 """
68 raise NotImplementedError()
70 @abstractmethod
71 def visit(self, name: str, builder: DimensionConstructionBuilder) -> None:
72 """Modify the given builder object to include responsible entities.
74 Parameters
75 ----------
76 name : `str`
77 Name of the entity being added.
78 builder : `DimensionConstructionBuilder`
79 Builder to modify in-place and from which dependencies can be
80 obtained.
82 Notes
83 -----
84 Subclasses may assume (and callers must guarantee) that
85 `hasDependenciesIn` would return `False` prior to `visit` being called.
86 """
87 raise NotImplementedError()
90class DimensionConstructionBuilder:
91 """A builder object for constructing `DimensionUniverse` instances.
93 `DimensionConstructionVisitor` objects can be added to a
94 `DimensionConstructionBuilder` object in any order, and are invoked
95 in a deterministic order consistent with their dependency relationships
96 by a single call (by the `DimensionUniverse`) to the `finish` method.
98 Parameters
99 ----------
100 version : `int`
101 Version for the `DimensionUniverse`.
102 commonSkyPixName : `str`
103 Name of the "common" skypix dimension that is used to relate all other
104 spatial `TopologicalRelationshipEndpoint` objects.
105 config : `DimensionConfig`
106 The dimension universe to be used.
107 namespace : `str`, optional
108 The namespace to assign to this universe.
109 """
111 def __init__(
112 self,
113 version: int,
114 commonSkyPixName: str,
115 config: DimensionConfig,
116 *,
117 namespace: str | None = None,
118 ) -> None:
119 self.dimensions = NamedValueSet()
120 self.elements = NamedValueSet()
121 self.topology = {space: NamedValueSet() for space in TopologicalSpace.__members__.values()}
122 self.version = version
123 self.namespace = namespace
124 self.config = config
125 self.commonSkyPixName = commonSkyPixName
126 self._todo: dict[str, DimensionConstructionVisitor] = {}
128 def add(self, name: str, visitor: DimensionConstructionVisitor) -> None:
129 """Add a single visitor to the builder.
131 Parameters
132 ----------
133 name : `str`
134 Name of the object the visitor creates.
135 visitor : `DimensionConstructionVisitor`
136 Visitor instance to add.
137 """
138 self._todo[name] = visitor
140 def finish(self) -> None:
141 """Complete construction of the builder.
143 This method invokes all visitors in an order consistent with their
144 dependencies, fully populating all public attributes. It should be
145 called only once, by `DimensionUniverse` itself.
146 """
147 while self._todo:
148 unblocked = [
149 name
150 for name, visitor in self._todo.items()
151 if not visitor.has_dependencies_in(self._todo.keys())
152 ]
153 unblocked.sort() # Break ties lexicographically.
154 if not unblocked:
155 raise RuntimeError(f"Cycle or unmet dependency in dimension elements: {self._todo.keys()}.")
156 for name in unblocked:
157 self._todo.pop(name).visit(name, self)
159 version: int
160 """Version number for the `DimensionUniverse` (`int`).
162 Populated at builder construction.
163 """
165 namespace: str | None
166 """Namespace for the `DimensionUniverse` (`str`)
168 Populated at builder construction.
169 """
171 commonSkyPixName: str
172 """Name of the common skypix dimension used to connect other spatial
173 `TopologicalRelationshipEndpoint` objects (`str`).
175 Populated at builder construction.
176 """
178 dimensions: NamedValueSet[Dimension]
179 """Set of all `Dimension` objects (`NamedValueSet` [ `Dimension` ]).
181 Populated by `finish`.
182 """
184 elements: NamedValueSet[DimensionElement]
185 """Set of all `DimensionElement` objects
186 (`NamedValueSet` [ `DimensionElement` ]).
188 Populated by `finish`. `DimensionConstructionVisitor` classes that
189 construct `Dimension` objects are responsible for adding them to this
190 set as well as `dimensions`.
191 """
193 topology: dict[TopologicalSpace, NamedValueSet[TopologicalFamily]]
194 """Dictionary containing all `TopologicalFamily` objects
195 (`dict` [ `TopologicalSpace`, `NamedValueSet` [ `TopologicalFamily` ] ] ).
196 """