Coverage for python/lsst/daf/butler/registry/interfaces/_opaque.py: 70%

38 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-08-31 04:05 -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/>. 

21from __future__ import annotations 

22 

23"""Interfaces for the objects that manage opaque (logical) tables within a 

24`Registry`. 

25""" 

26 

27__all__ = ["OpaqueTableStorageManager", "OpaqueTableStorage"] 

28 

29from abc import ABC, abstractmethod 

30from typing import Any, Iterable, Iterator, Optional 

31 

32from ...core.ddl import TableSpec 

33from ._database import Database, StaticTablesContext 

34from ._versioning import VersionedExtension 

35 

36 

37class OpaqueTableStorage(ABC): 

38 """An interface that manages the records associated with a particular 

39 opaque table in a `Registry`. 

40 

41 Parameters 

42 ---------- 

43 name : `str` 

44 Name of the opaque table. 

45 """ 

46 

47 def __init__(self, name: str): 

48 self.name = name 

49 

50 @abstractmethod 

51 def insert(self, *data: dict) -> None: 

52 """Insert records into the table 

53 

54 Parameters 

55 ---------- 

56 *data 

57 Each additional positional argument is a dictionary that represents 

58 a single row to be added. 

59 """ 

60 raise NotImplementedError() 

61 

62 @abstractmethod 

63 def fetch(self, **where: Any) -> Iterator[dict]: 

64 """Retrieve records from an opaque table. 

65 

66 Parameters 

67 ---------- 

68 **where 

69 Additional keyword arguments are interpreted as equality 

70 constraints that restrict the returned rows (combined with AND); 

71 keyword arguments are column names and values are the values they 

72 must have. 

73 

74 Yields 

75 ------ 

76 row : `dict` 

77 A dictionary representing a single result row. 

78 """ 

79 raise NotImplementedError() 

80 

81 @abstractmethod 

82 def delete(self, columns: Iterable[str], *rows: dict) -> None: 

83 """Remove records from an opaque table. 

84 

85 Parameters 

86 ---------- 

87 columns: `~collections.abc.Iterable` of `str` 

88 The names of columns that will be used to constrain the rows to 

89 be deleted; these will be combined via ``AND`` to form the 

90 ``WHERE`` clause of the delete query. 

91 *rows 

92 Positional arguments are the keys of rows to be deleted, as 

93 dictionaries mapping column name to value. The keys in all 

94 dictionaries must be exactly the names in ``columns``. 

95 """ 

96 raise NotImplementedError() 

97 

98 name: str 

99 """The name of the logical table this instance manages (`str`). 

100 """ 

101 

102 

103class OpaqueTableStorageManager(VersionedExtension): 

104 """An interface that manages the opaque tables in a `Registry`. 

105 

106 `OpaqueTableStorageManager` primarily serves as a container and factory for 

107 `OpaqueTableStorage` instances, which each provide access to the records 

108 for a different (logical) opaque table. 

109 

110 Notes 

111 ----- 

112 Opaque tables are primarily used by `Datastore` instances to manage their 

113 internal data in the same database that hold the `Registry`, but are not 

114 limited to this. 

115 

116 While an opaque table in a multi-layer `Registry` may in fact be the union 

117 of multiple tables in different layers, we expect this to be rare, as 

118 `Registry` layers will typically correspond to different leaf `Datastore` 

119 instances (each with their own opaque table) in a `ChainedDatastore`. 

120 """ 

121 

122 @classmethod 

123 @abstractmethod 

124 def initialize(cls, db: Database, context: StaticTablesContext) -> OpaqueTableStorageManager: 

125 """Construct an instance of the manager. 

126 

127 Parameters 

128 ---------- 

129 db : `Database` 

130 Interface to the underlying database engine and namespace. 

131 context : `StaticTablesContext` 

132 Context object obtained from `Database.declareStaticTables`; used 

133 to declare any tables that should always be present in a layer 

134 implemented with this manager. 

135 

136 Returns 

137 ------- 

138 manager : `OpaqueTableStorageManager` 

139 An instance of a concrete `OpaqueTableStorageManager` subclass. 

140 """ 

141 raise NotImplementedError() 

142 

143 def __getitem__(self, name: str) -> OpaqueTableStorage: 

144 """Interface to `get` that raises `LookupError` instead of returning 

145 `None` on failure. 

146 """ 

147 r = self.get(name) 

148 if r is None: 

149 raise LookupError(f"No logical table with name '{name}' found.") 

150 return r 

151 

152 @abstractmethod 

153 def get(self, name: str) -> Optional[OpaqueTableStorage]: 

154 """Return an object that provides access to the records associated with 

155 an opaque logical table. 

156 

157 Parameters 

158 ---------- 

159 name : `str` 

160 Name of the logical table. 

161 

162 Returns 

163 ------- 

164 records : `OpaqueTableStorage` or `None` 

165 The object representing the records for the given table in this 

166 layer, or `None` if there are no records for that table in this 

167 layer. 

168 

169 Notes 

170 ----- 

171 Opaque tables must be registered with the layer (see `register`) by 

172 the same client before they can safely be retrieved with `get`. 

173 Unlike most other manager classes, the set of opaque tables cannot be 

174 obtained from an existing data repository. 

175 """ 

176 raise NotImplementedError() 

177 

178 @abstractmethod 

179 def register(self, name: str, spec: TableSpec) -> OpaqueTableStorage: 

180 """Ensure that this layer can hold records for the given opaque logical 

181 table, creating new tables as necessary. 

182 

183 Parameters 

184 ---------- 

185 name : `str` 

186 Name of the logical table. 

187 spec : `TableSpec` 

188 Schema specification for the table to be created. 

189 

190 Returns 

191 ------- 

192 records : `OpaqueTableStorage` 

193 The object representing the records for the given element in this 

194 layer. 

195 

196 Notes 

197 ----- 

198 This operation may not be invoked within a transaction context block. 

199 """ 

200 raise NotImplementedError()