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