Coverage for python/lsst/daf/butler/registry/obscore/_spatial.py: 58%
35 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-08-12 09:20 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-08-12 09:20 +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
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(cls, *, name: str, config: Mapping[str, Any], db: Database | None) -> SpatialObsCorePlugin:
62 """Construct an instance of the plugin.
64 Parameters
65 ----------
66 name : `str`
67 Arbitrary name given to this plugin (usually key in
68 configuration).
69 config : `dict` [ `str`, `Any` ]
70 Plugin configuration dictionary.
71 db : `Database`, optional
72 Interface to the underlying database engine and namespace. In some
73 contexts the database may not be available and will be set to
74 `None`. If plugin class requires access to the database, it should
75 raise a `MissingDatabaseError` exception when ``db`` is `None`.
77 Returns
78 -------
79 manager : `ObsCoreTableManager`
80 Plugin instance.
82 Raises
83 ------
84 MissingDatabaseError
85 Raised if plugin requires access to database, but ``db`` is `None`.
86 """
87 raise NotImplementedError()
89 @abstractmethod
90 def extend_table_spec(self, table_spec: ddl.TableSpec) -> None:
91 """Update obscore table specification with any new columns that are
92 needed for this plugin.
94 Parameters
95 ----------
96 table : `ddl.TableSpec`
97 ObsCore table specification.
99 Notes
100 -----
101 Table specification is updated in place. Plugins should not remove
102 columns, normally updates are limited to adding new columns or indices.
103 """
104 raise NotImplementedError()
106 @abstractmethod
107 def make_records(self, region: Region | None) -> Record | None:
108 """Return data for obscore records corresponding to a given region.
110 Parameters
111 ----------
112 region : `Region`, optional
113 Spatial region, can be `None` if dataset has no associated region.
115 Returns
116 -------
117 record : `dict` [ `str`, `Any` ] or `None`
118 Data to store in the main obscore table with column values
119 corresponding to a region or `None` if there is nothing to store.
121 Raises
122 ------
123 RegionTypeError
124 Raised if type of the region is not supported.
125 """
126 raise NotImplementedError()
128 @classmethod
129 def load_plugins(
130 cls, config: Mapping[str, SpatialPluginConfig], db: Database | None
131 ) -> Sequence[SpatialObsCorePlugin]:
132 """Load all plugins based on plugin configurations.
134 Parameters
135 ----------
136 config : `~collections.abc.Mapping` [ `str`, `SpatialPluginConfig` ]
137 Configuration for plugins. The key is an arbitrary name and the
138 value is an object describing plugin class and its configuration
139 options.
140 db : `Database`, optional
141 Interface to the underlying database engine and namespace.
143 Raises
144 ------
145 MissingDatabaseError
146 Raised if one of the plugins requires access to database, but
147 ``db`` is `None`.
148 """
149 # We have to always load default plugin even if it does not appear in
150 # configuration.
151 from .default_spatial import DefaultSpatialObsCorePlugin
153 spatial_plugins: list[SpatialObsCorePlugin] = []
154 has_default = False
155 for plugin_name, plugin_config in config.items():
156 class_name = plugin_config.cls
157 plugin_class = doImportType(class_name)
158 if not issubclass(plugin_class, SpatialObsCorePlugin):
159 raise TypeError(
160 f"Spatial ObsCore plugin {class_name} is not a subclass of SpatialObsCorePlugin"
161 )
162 plugin = plugin_class.initialize(name=plugin_name, config=plugin_config.config, db=db)
163 spatial_plugins.append(plugin)
164 if plugin_class is DefaultSpatialObsCorePlugin:
165 has_default = True
167 if not has_default:
168 # Always create default spatial plugin.
169 spatial_plugins.insert(
170 0, DefaultSpatialObsCorePlugin.initialize(name="default", config={}, db=db)
171 )
173 return spatial_plugins