Coverage for python/lsst/daf/butler/registry/obscore/_spatial.py: 58%
35 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-26 02:48 -0700
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-26 02:48 -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
30__all__ = ["MissingDatabaseError", "RegionTypeWarning", "SpatialObsCorePlugin"]
32from abc import ABC, abstractmethod
33from collections.abc import Mapping, Sequence
34from typing import TYPE_CHECKING, Any
36from lsst.sphgeom import Region
37from lsst.utils import doImportType
39if TYPE_CHECKING:
40 from ... import ddl
41 from ..interfaces import Database
42 from ._config import SpatialPluginConfig
43 from ._records import Record
46class MissingDatabaseError(Exception):
47 """Exception raised when database is not provided but plugin implementation
48 requires it.
49 """
52class RegionTypeError(TypeError):
53 """Exception raised for unsupported region types."""
56class RegionTypeWarning(Warning):
57 """Warning category for unsupported region types."""
60class SpatialObsCorePlugin(ABC):
61 """Interface for classes that implement support for spatial columns and
62 indices in obscore table.
63 """
65 @classmethod
66 @abstractmethod
67 def initialize(cls, *, name: str, config: Mapping[str, Any], db: Database | None) -> SpatialObsCorePlugin:
68 """Construct an instance of the plugin.
70 Parameters
71 ----------
72 name : `str`
73 Arbitrary name given to this plugin (usually key in
74 configuration).
75 config : `dict` [ `str`, `Any` ]
76 Plugin configuration dictionary.
77 db : `Database`, optional
78 Interface to the underlying database engine and namespace. In some
79 contexts the database may not be available and will be set to
80 `None`. If plugin class requires access to the database, it should
81 raise a `MissingDatabaseError` exception when ``db`` is `None`.
83 Returns
84 -------
85 manager : `ObsCoreTableManager`
86 Plugin instance.
88 Raises
89 ------
90 MissingDatabaseError
91 Raised if plugin requires access to database, but ``db`` is `None`.
92 """
93 raise NotImplementedError()
95 @abstractmethod
96 def extend_table_spec(self, table_spec: ddl.TableSpec) -> None:
97 """Update obscore table specification with any new columns that are
98 needed for this plugin.
100 Parameters
101 ----------
102 table_spec : `ddl.TableSpec`
103 ObsCore table specification.
105 Notes
106 -----
107 Table specification is updated in place. Plugins should not remove
108 columns, normally updates are limited to adding new columns or indices.
109 """
110 raise NotImplementedError()
112 @abstractmethod
113 def make_records(self, region: Region | None) -> Record | None:
114 """Return data for obscore records corresponding to a given region.
116 Parameters
117 ----------
118 region : `Region`, optional
119 Spatial region, can be `None` if dataset has no associated region.
121 Returns
122 -------
123 record : `dict` [ `str`, `Any` ] or `None`
124 Data to store in the main obscore table with column values
125 corresponding to a region or `None` if there is nothing to store.
127 Raises
128 ------
129 RegionTypeError
130 Raised if type of the region is not supported.
131 """
132 raise NotImplementedError()
134 @classmethod
135 def load_plugins(
136 cls, config: Mapping[str, SpatialPluginConfig], db: Database | None
137 ) -> Sequence[SpatialObsCorePlugin]:
138 """Load all plugins based on plugin configurations.
140 Parameters
141 ----------
142 config : `~collections.abc.Mapping` [ `str`, `SpatialPluginConfig` ]
143 Configuration for plugins. The key is an arbitrary name and the
144 value is an object describing plugin class and its configuration
145 options.
146 db : `Database`, optional
147 Interface to the underlying database engine and namespace.
149 Raises
150 ------
151 MissingDatabaseError
152 Raised if one of the plugins requires access to database, but
153 ``db`` is `None`.
154 """
155 # We have to always load default plugin even if it does not appear in
156 # configuration.
157 from .default_spatial import DefaultSpatialObsCorePlugin
159 spatial_plugins: list[SpatialObsCorePlugin] = []
160 has_default = False
161 for plugin_name, plugin_config in config.items():
162 class_name = plugin_config.cls
163 plugin_class = doImportType(class_name)
164 if not issubclass(plugin_class, SpatialObsCorePlugin):
165 raise TypeError(
166 f"Spatial ObsCore plugin {class_name} is not a subclass of SpatialObsCorePlugin"
167 )
168 plugin = plugin_class.initialize(name=plugin_name, config=plugin_config.config, db=db)
169 spatial_plugins.append(plugin)
170 if plugin_class is DefaultSpatialObsCorePlugin:
171 has_default = True
173 if not has_default:
174 # Always create default spatial plugin.
175 spatial_plugins.insert(
176 0, DefaultSpatialObsCorePlugin.initialize(name="default", config={}, db=db)
177 )
179 return spatial_plugins