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

43 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-02-02 14:18 +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, Optional 

29 

30from lsst.daf.butler import DatasetId 

31from lsst.sphgeom import Region 

32from lsst.utils import doImportType 

33 

34if TYPE_CHECKING: 34 ↛ 35line 34 didn't jump to line 35, because the condition on line 34 was never true

35 from ...core import ddl 

36 from ..interfaces import Database 

37 from ._config import SpatialPluginConfig 

38 from ._records import Record 

39 

40 

41class MissingDatabaseError(Exception): 

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

43 requires it. 

44 """ 

45 

46 

47class RegionTypeWarning(Warning): 

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

49 

50 

51class SpatialObsCorePlugin(ABC): 

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

53 indices in obscore table. 

54 """ 

55 

56 @classmethod 

57 @abstractmethod 

58 def initialize( 

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

60 ) -> SpatialObsCorePlugin: 

61 """Construct an instance of the plugin. 

62 

63 Parameters 

64 ---------- 

65 name : `str` 

66 Arbitrary name given to this plugin (usually key in 

67 configuration). 

68 config : `dict` [ `str`, `Any` ] 

69 Plugin configuration dictionary. 

70 db : `Database`, optional 

71 Interface to the underlying database engine and namespace. In some 

72 contexts the database may not be available and will be set to 

73 `None`. If plugin class requires access to the database, it should 

74 raise a `MissingDatabaseError` exception when ``db`` is `None`. 

75 

76 Returns 

77 ------- 

78 manager : `ObsCoreTableManager` 

79 Plugin instance. 

80 

81 Raises 

82 ------ 

83 MissingDatabaseError 

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

85 """ 

86 raise NotImplementedError() 

87 

88 @abstractmethod 

89 def extend_table_spec(self, table_spec: ddl.TableSpec) -> None: 

90 """Update obscore table specification with any new columns that are 

91 needed for this plugin. 

92 

93 Parameters 

94 ---------- 

95 table : `ddl.TableSpec` 

96 ObsCore table specification. 

97 

98 Notes 

99 ----- 

100 Table specification is updated in place. Plugins should not remove 

101 columns, normally updates are limited to adding new columns or indices. 

102 """ 

103 raise NotImplementedError() 

104 

105 @abstractmethod 

106 def make_records(self, dataset_id: DatasetId, region: Optional[Region]) -> Optional[Record]: 

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

108 

109 Parameters 

110 ---------- 

111 dataset_id : `DatasetId` 

112 ID of the corresponding dataset. 

113 region : `Region`, optional 

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

115 

116 Returns 

117 ------- 

118 record : `dict` [ `str`, `Any` ] or `None` 

119 Data to store in the main obscore table with column values 

120 corresponding to a region or `None` if there is nothing to store. 

121 """ 

122 raise NotImplementedError() 

123 

124 @classmethod 

125 def load_plugins( 

126 cls, config: Mapping[str, SpatialPluginConfig], db: Optional[Database] 

127 ) -> Sequence[SpatialObsCorePlugin]: 

128 """Load all plugins based on plugin configurations. 

129 

130 Parameters 

131 ---------- 

132 config : `Mapping` [ `str`, `SpatialPluginConfig` ] 

133 Configuration for plugins. The key is an arbitrary name and the 

134 value is an object describing plugin class and its configuration 

135 options. 

136 db : `Database`, optional 

137 Interface to the underlying database engine and namespace. 

138 

139 Raises 

140 ------ 

141 MissingDatabaseError 

142 Raised if one of the plugins requires access to database, but 

143 ``db`` is `None`. 

144 """ 

145 # We have to always load default plugin even if it does not appear in 

146 # configuration. 

147 from .default_spatial import DefaultSpatialObsCorePlugin 

148 

149 spatial_plugins: list[SpatialObsCorePlugin] = [] 

150 has_default = False 

151 for plugin_name, plugin_config in config.items(): 

152 class_name = plugin_config.cls 

153 plugin_class = doImportType(class_name) 

154 if not issubclass(plugin_class, SpatialObsCorePlugin): 

155 raise TypeError( 

156 f"Spatial ObsCore plugin {class_name} is not a subclass of SpatialObsCorePlugin" 

157 ) 

158 plugin = plugin_class.initialize(name=plugin_name, config=plugin_config.config, db=db) 

159 spatial_plugins.append(plugin) 

160 if plugin_class is DefaultSpatialObsCorePlugin: 

161 has_default = True 

162 

163 if not has_default: 

164 # Always create default spatial plugin. 

165 spatial_plugins.insert( 

166 0, DefaultSpatialObsCorePlugin.initialize(name="default", config={}, db=db) 

167 ) 

168 

169 return spatial_plugins