Coverage for python/lsst/daf/butler/registry/_defaults.py: 29%

52 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-08-05 01:26 +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/>. 

21 

22from __future__ import annotations 

23 

24__all__ = ("RegistryDefaults",) 

25 

26import contextlib 

27from collections.abc import Sequence, Set 

28from typing import TYPE_CHECKING, Any 

29 

30from lsst.utils.classes import immutable 

31 

32from ..core import DataCoordinate 

33from ._collection_summary import CollectionSummary 

34from ._exceptions import MissingCollectionError 

35from .wildcards import CollectionWildcard 

36 

37if TYPE_CHECKING: 

38 from ._registry import Registry 

39 

40 

41@immutable 

42class RegistryDefaults: 

43 """A struct used to provide the default collections searched or written to 

44 by a `Registry` or `Butler` instance. 

45 

46 Parameters 

47 ---------- 

48 collections : `str` or `~collections.abc.Iterable` [ `str` ], optional 

49 An expression specifying the collections to be searched (in order) when 

50 reading datasets. If a default value for a governor dimension is not 

51 given via ``**kwargs``, and exactly one value for that dimension 

52 appears in the datasets in ``collections``, that value is also used as 

53 the default for that dimension. 

54 This may be a `str` collection name or an iterable thereof. 

55 See :ref:`daf_butler_collection_expressions` for more information. 

56 These collections are not registered automatically and must be 

57 manually registered before they are used by any `Registry` or `Butler` 

58 method, but they may be manually registered after a `Registry` or 

59 `Butler` is initialized with this struct. 

60 run : `str`, optional 

61 Name of the `~CollectionType.RUN` collection new datasets should be 

62 inserted into. If ``collections`` is `None` and ``run`` is not `None`, 

63 ``collections`` will be set to ``[run]``. If not `None`, this 

64 collection will automatically be registered when the default struct is 

65 attached to a `Registry` instance. 

66 infer : `bool`, optional 

67 If `True` (default) infer default data ID values from the values 

68 present in the datasets in ``collections``: if all collections have the 

69 same value (or no value) for a governor dimension, that value will be 

70 the default for that dimension. Nonexistent collections are ignored. 

71 If a default value is provided explicitly for a governor dimension via 

72 ``**kwargs``, no default will be inferred for that dimension. 

73 **kwargs : `str` 

74 Default data ID key-value pairs. These may only identify "governor" 

75 dimensions like ``instrument`` and ``skymap``, though this is only 

76 checked when the defaults struct is actually attached to a `Registry`. 

77 """ 

78 

79 def __init__(self, collections: Any = None, run: str | None = None, infer: bool = True, **kwargs: str): 

80 if collections is None: 

81 if run is not None: 

82 collections = (run,) 

83 else: 

84 collections = () 

85 self.collections = CollectionWildcard.from_expression(collections).require_ordered() 

86 self.run = run 

87 self._infer = infer 

88 self._kwargs = kwargs 

89 

90 def __repr__(self) -> str: 

91 collections = f"collections={self.collections!r}" if self.collections else "" 

92 run = f"run={self.run!r}" if self.run else "" 

93 if self._kwargs: 

94 kwargs = ", ".join([f"{k}={v!r}" for k, v in self._kwargs.items()]) 

95 else: 

96 kwargs = "" 

97 args = ", ".join([arg for arg in (collections, run, kwargs) if arg]) 

98 return f"{type(self).__name__}({args})" 

99 

100 def finish(self, registry: Registry) -> None: 

101 """Validate the defaults struct and standardize its data ID. 

102 

103 This should be called only by a `Registry` instance when the defaults 

104 struct is first associated with it. 

105 

106 Parameters 

107 ---------- 

108 registry : `Registry` 

109 Registry instance these defaults are being attached to. 

110 

111 Raises 

112 ------ 

113 TypeError 

114 Raised if a non-governor dimension was included in ``**kwargs`` 

115 at construction. 

116 """ 

117 allGovernorDimensions = registry.dimensions.getGovernorDimensions() 

118 if not self._kwargs.keys() <= allGovernorDimensions.names: 

119 raise TypeError( 

120 "Only governor dimensions may be identified by a default data " 

121 f"ID, not {self._kwargs.keys() - allGovernorDimensions.names}. " 

122 "(These may just be unrecognized keyword arguments passed at " 

123 "Butler construction.)" 

124 ) 

125 if self._infer and self._kwargs.keys() != allGovernorDimensions.names: 

126 summaries = [] 

127 for collection in self.collections: 

128 with contextlib.suppress(MissingCollectionError): 

129 summaries.append(registry.getCollectionSummary(collection)) 

130 

131 if summaries: 

132 summary = CollectionSummary.union(*summaries) 

133 for dimensionName in allGovernorDimensions.names - self._kwargs.keys(): 

134 values: Set[str] = summary.governors.get(dimensionName, frozenset()) 

135 if len(values) == 1: 

136 (value,) = values 

137 self._kwargs[dimensionName] = value 

138 self.dataId = registry.expandDataId(self._kwargs, withDefaults=False) 

139 

140 collections: Sequence[str] 

141 """The collections to search by default, in order 

142 (`~collections.abc.Sequence` [ `str` ]). 

143 """ 

144 

145 run: str | None 

146 """Name of the run this butler writes outputs to by default (`str` or 

147 `None`). 

148 """ 

149 

150 dataId: DataCoordinate 

151 """The default data ID (`DataCoordinate`). 

152 

153 Dimensions without defaults are simply not included. Only governor 

154 dimensions are ever included in defaults. 

155 

156 This attribute may not be accessed before the defaults struct is 

157 attached to a `Registry` instance. It always satisfies both ``hasFull`` 

158 and ``hasRecords``. 

159 """