Coverage for python/lsst/daf/butler/core/dimensions/construction.py : 39%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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 (
26 AbstractSet,
27 Dict,
28 Iterable,
29 TYPE_CHECKING,
30)
32from ..named import NamedValueSet
33from .._topology import TopologicalFamily, TopologicalSpace
35if TYPE_CHECKING: 35 ↛ 36line 35 didn't jump to line 36, because the condition on line 35 was never true
36 from ._elements import Dimension, DimensionElement
37 from ._packer import DimensionPackerFactory
40class DimensionConstructionVisitor(ABC):
41 """An abstract base class for adding one or more entities to a
42 `DimensionConstructionBuilder`.
44 Parameters
45 ----------
46 name : `str`
47 Name of an entity being added. This must be unique across all
48 entities, which include `DimensionElement`, `TopologicalFamily`, and
49 `DimensionPackerFactory` objects. The visitor may add other entities
50 as well, as long as only the named entity is referenced by other
51 entities in the universe.
52 """
54 def __init__(self, name: str):
55 self.name = name
57 def __str__(self) -> str:
58 return self.name
60 @abstractmethod
61 def hasDependenciesIn(self, others: AbstractSet[str]) -> bool:
62 """Test 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 the entities this object
82 is responsible for constructing.
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 `DimensionUnvierse`) 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 visitors : `Iterable` [ `DimensionConstructionVisitor` ]
115 Visitor instances to include from the start.
116 """
118 def __init__(
119 self,
120 version: int,
121 commonSkyPixName: str, *,
122 visitors: Iterable[DimensionConstructionVisitor] = ()
123 ) -> None:
124 self.dimensions = NamedValueSet()
125 self.elements = NamedValueSet()
126 self.topology = {space: NamedValueSet() for space in TopologicalSpace.__members__.values()}
127 self.packers = {}
128 self.version = version
129 self.commonSkyPixName = commonSkyPixName
130 self._todo: Dict[str, DimensionConstructionVisitor] = {v.name: v for v in visitors}
132 def add(self, visitor: DimensionConstructionVisitor) -> None:
133 """Add a single visitor to the builder.
135 Parameters
136 ----------
137 visitor : `DimensionConstructionVisitor`
138 Visitor instance to add.
139 """
140 self._todo[visitor.name] = visitor
142 def update(self, visitors: Iterable[DimensionConstructionVisitor]) -> None:
143 """Add multiple visitors to the builder.
145 Parameters
146 ----------
147 visitors : `Iterable` [ `DimensionConstructionVisitor` ]
148 Visitor instances to add.
149 """
150 self._todo.update((v.name, v) for v in visitors)
152 def finish(self) -> None:
153 """Complete construction of the builder.
155 This method invokes all visitors in an order consistent with their
156 dependencies, fully populating all public attributes. It should be
157 called only once, by `DimensionUniverse` itself.
158 """
159 while self._todo:
160 unblocked = [name for name, visitor in self._todo.items()
161 if not visitor.hasDependenciesIn(self._todo.keys())]
162 unblocked.sort() # Break ties lexicographically.
163 if not unblocked:
164 raise RuntimeError(f"Cycle or unmet dependency in dimension elements: {self._todo.keys()}.")
165 for name in unblocked:
166 self._todo.pop(name).visit(self)
168 version: int
169 """Version number for the `DimensionUniverse` (`int`).
171 Populated at builder construction.
172 """
174 commonSkyPixName: str
175 """Name of the common skypix dimension used to connect other spatial
176 `TopologicalRelationshipEndpoint` objects (`str`).
178 Populated at builder construction.
179 """
181 dimensions: NamedValueSet[Dimension]
182 """Set of all `Dimension` objects (`NamedValueSet` [ `Dimension` ]).
184 Populated by `finish`.
185 """
187 elements: NamedValueSet[DimensionElement]
188 """Set of all `DimensionElement` objects
189 (`NamedValueSet` [ `DimensionElement` ]).
191 Populated by `finish`. `DimensionConstructionVisitor` classes that
192 construct `Dimension` objects are responsible for adding them to this
193 set as well as `dimensions`.
194 """
196 topology: Dict[TopologicalSpace, NamedValueSet[TopologicalFamily]]
197 """Dictionary containing all `TopologicalFamily` objects
198 (`dict` [ `TopologicalSpace`, `NamedValueSet` [ `TopologicalFamily` ] ] ).
199 """
201 packers: Dict[str, DimensionPackerFactory]
202 """Dictionary containing all `DimensionPackerFactory` objects
203 (`dict` [ `str`, `DimensionPackerFactory` ] ).
204 """