Coverage for python/lsst/daf/butler/registry/obscore/_spatial.py: 47%

35 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-06 02:34 -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 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, Optional 

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( 

62 cls, *, name: str, config: Mapping[str, Any], db: Optional[Database] 

63 ) -> SpatialObsCorePlugin: 

64 """Construct an instance of the plugin. 

65 

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

78 

79 Returns 

80 ------- 

81 manager : `ObsCoreTableManager` 

82 Plugin instance. 

83 

84 Raises 

85 ------ 

86 MissingDatabaseError 

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

88 """ 

89 raise NotImplementedError() 

90 

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. 

95 

96 Parameters 

97 ---------- 

98 table : `ddl.TableSpec` 

99 ObsCore table specification. 

100 

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

107 

108 @abstractmethod 

109 def make_records(self, region: Optional[Region]) -> Optional[Record]: 

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

111 

112 Parameters 

113 ---------- 

114 region : `Region`, optional 

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

116 

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. 

122 

123 Raises 

124 ------ 

125 RegionTypeError 

126 Raised if type of the region is not supported. 

127 """ 

128 raise NotImplementedError() 

129 

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. 

135 

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. 

144 

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 

154 

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 

168 

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 ) 

174 

175 return spatial_plugins