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-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__ = ["MissingDatabaseError", "RegionTypeWarning", "SpatialObsCorePlugin"] 

25 

26from abc import ABC, abstractmethod 

27from collections.abc import Mapping, Sequence 

28from typing import TYPE_CHECKING, Any 

29 

30from lsst.sphgeom import Region 

31from lsst.utils import doImportType 

32 

33if TYPE_CHECKING: 

34 from ...core import ddl 

35 from ..interfaces import Database 

36 from ._config import SpatialPluginConfig 

37 from ._records import Record 

38 

39 

40class MissingDatabaseError(Exception): 

41 """Exception raised when database is not provided but plugin implementation 

42 requires it. 

43 """ 

44 

45 

46class RegionTypeError(TypeError): 

47 """Exception raised for unsupported region types.""" 

48 

49 

50class RegionTypeWarning(Warning): 

51 """Warning category for unsupported region types.""" 

52 

53 

54class SpatialObsCorePlugin(ABC): 

55 """Interface for classes that implement support for spatial columns and 

56 indices in obscore table. 

57 """ 

58 

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. 

63 

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`. 

76 

77 Returns 

78 ------- 

79 manager : `ObsCoreTableManager` 

80 Plugin instance. 

81 

82 Raises 

83 ------ 

84 MissingDatabaseError 

85 Raised if plugin requires access to database, but ``db`` is `None`. 

86 """ 

87 raise NotImplementedError() 

88 

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. 

93 

94 Parameters 

95 ---------- 

96 table : `ddl.TableSpec` 

97 ObsCore table specification. 

98 

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() 

105 

106 @abstractmethod 

107 def make_records(self, region: Region | None) -> Record | None: 

108 """Return data for obscore records corresponding to a given region. 

109 

110 Parameters 

111 ---------- 

112 region : `Region`, optional 

113 Spatial region, can be `None` if dataset has no associated region. 

114 

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. 

120 

121 Raises 

122 ------ 

123 RegionTypeError 

124 Raised if type of the region is not supported. 

125 """ 

126 raise NotImplementedError() 

127 

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. 

133 

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. 

142 

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 

152 

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 

166 

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 ) 

172 

173 return spatial_plugins