Coverage for python/lsst/daf/butler/registry/_defaults.py: 29%
52 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-10-02 08:00 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-10-02 08:00 +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 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
30__all__ = ("RegistryDefaults",)
32import contextlib
33from collections.abc import Sequence, Set
34from typing import TYPE_CHECKING, Any
36from lsst.utils.classes import immutable
38from ..core import DataCoordinate
39from ._collection_summary import CollectionSummary
40from ._exceptions import MissingCollectionError
41from .wildcards import CollectionWildcard
43if TYPE_CHECKING:
44 from ._registry import Registry
47@immutable
48class RegistryDefaults:
49 """A struct used to provide the default collections searched or written to
50 by a `Registry` or `Butler` instance.
52 Parameters
53 ----------
54 collections : `str` or `~collections.abc.Iterable` [ `str` ], optional
55 An expression specifying the collections to be searched (in order) when
56 reading datasets. If a default value for a governor dimension is not
57 given via ``**kwargs``, and exactly one value for that dimension
58 appears in the datasets in ``collections``, that value is also used as
59 the default for that dimension.
60 This may be a `str` collection name or an iterable thereof.
61 See :ref:`daf_butler_collection_expressions` for more information.
62 These collections are not registered automatically and must be
63 manually registered before they are used by any `Registry` or `Butler`
64 method, but they may be manually registered after a `Registry` or
65 `Butler` is initialized with this struct.
66 run : `str`, optional
67 Name of the `~CollectionType.RUN` collection new datasets should be
68 inserted into. If ``collections`` is `None` and ``run`` is not `None`,
69 ``collections`` will be set to ``[run]``. If not `None`, this
70 collection will automatically be registered when the default struct is
71 attached to a `Registry` instance.
72 infer : `bool`, optional
73 If `True` (default) infer default data ID values from the values
74 present in the datasets in ``collections``: if all collections have the
75 same value (or no value) for a governor dimension, that value will be
76 the default for that dimension. Nonexistent collections are ignored.
77 If a default value is provided explicitly for a governor dimension via
78 ``**kwargs``, no default will be inferred for that dimension.
79 **kwargs : `str`
80 Default data ID key-value pairs. These may only identify "governor"
81 dimensions like ``instrument`` and ``skymap``, though this is only
82 checked when the defaults struct is actually attached to a `Registry`.
83 """
85 def __init__(self, collections: Any = None, run: str | None = None, infer: bool = True, **kwargs: str):
86 if collections is None:
87 if run is not None:
88 collections = (run,)
89 else:
90 collections = ()
91 self.collections = CollectionWildcard.from_expression(collections).require_ordered()
92 self.run = run
93 self._infer = infer
94 self._kwargs = kwargs
96 def __repr__(self) -> str:
97 collections = f"collections={self.collections!r}" if self.collections else ""
98 run = f"run={self.run!r}" if self.run else ""
99 if self._kwargs:
100 kwargs = ", ".join([f"{k}={v!r}" for k, v in self._kwargs.items()])
101 else:
102 kwargs = ""
103 args = ", ".join([arg for arg in (collections, run, kwargs) if arg])
104 return f"{type(self).__name__}({args})"
106 def finish(self, registry: Registry) -> None:
107 """Validate the defaults struct and standardize its data ID.
109 This should be called only by a `Registry` instance when the defaults
110 struct is first associated with it.
112 Parameters
113 ----------
114 registry : `Registry`
115 Registry instance these defaults are being attached to.
117 Raises
118 ------
119 TypeError
120 Raised if a non-governor dimension was included in ``**kwargs``
121 at construction.
122 """
123 allGovernorDimensions = registry.dimensions.getGovernorDimensions()
124 if not self._kwargs.keys() <= allGovernorDimensions.names:
125 raise TypeError(
126 "Only governor dimensions may be identified by a default data "
127 f"ID, not {self._kwargs.keys() - allGovernorDimensions.names}. "
128 "(These may just be unrecognized keyword arguments passed at "
129 "Butler construction.)"
130 )
131 if self._infer and self._kwargs.keys() != allGovernorDimensions.names:
132 summaries = []
133 for collection in self.collections:
134 with contextlib.suppress(MissingCollectionError):
135 summaries.append(registry.getCollectionSummary(collection))
137 if summaries:
138 summary = CollectionSummary.union(*summaries)
139 for dimensionName in allGovernorDimensions.names - self._kwargs.keys():
140 values: Set[str] = summary.governors.get(dimensionName, frozenset())
141 if len(values) == 1:
142 (value,) = values
143 self._kwargs[dimensionName] = value
144 self.dataId = registry.expandDataId(self._kwargs, withDefaults=False)
146 collections: Sequence[str]
147 """The collections to search by default, in order
148 (`~collections.abc.Sequence` [ `str` ]).
149 """
151 run: str | None
152 """Name of the run this butler writes outputs to by default (`str` or
153 `None`).
154 """
156 dataId: DataCoordinate
157 """The default data ID (`DataCoordinate`).
159 Dimensions without defaults are simply not included. Only governor
160 dimensions are ever included in defaults.
162 This attribute may not be accessed before the defaults struct is
163 attached to a `Registry` instance. It always satisfies both ``hasFull``
164 and ``hasRecords``.
165 """