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

Shortcuts on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

37 statements  

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"""Interfaces for the objects that manage opaque (logical) tables within a 

23`Registry`. 

24""" 

25 

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

27 

28from abc import ABC, abstractmethod 

29from typing import ( 

30 Any, 

31 Iterable, 

32 Iterator, 

33 Optional, 

34) 

35 

36from ...core.ddl import TableSpec 

37from ._database import Database, StaticTablesContext 

38from ._versioning import VersionedExtension 

39 

40 

41class OpaqueTableStorage(ABC): 

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

43 opaque table in a `Registry`. 

44 

45 Parameters 

46 ---------- 

47 name : `str` 

48 Name of the opaque table. 

49 """ 

50 def __init__(self, name: str): 

51 self.name = name 

52 

53 @abstractmethod 

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

55 """Insert records into the table 

56 

57 Parameters 

58 ---------- 

59 *data 

60 Each additional positional argument is a dictionary that represents 

61 a single row to be added. 

62 """ 

63 raise NotImplementedError() 

64 

65 @abstractmethod 

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

67 """Retrieve records from an opaque table. 

68 

69 Parameters 

70 ---------- 

71 **where 

72 Additional keyword arguments are interpreted as equality 

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

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

75 must have. 

76 

77 Yields 

78 ------ 

79 row : `dict` 

80 A dictionary representing a single result row. 

81 """ 

82 raise NotImplementedError() 

83 

84 @abstractmethod 

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

86 """Remove records from an opaque table. 

87 

88 Parameters 

89 ---------- 

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

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

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

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

94 *rows 

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

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

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

98 """ 

99 raise NotImplementedError() 

100 

101 name: str 

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

103 """ 

104 

105 

106class OpaqueTableStorageManager(VersionedExtension): 

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

108 

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

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

111 for a different (logical) opaque table. 

112 

113 Notes 

114 ----- 

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

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

117 limited to this. 

118 

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

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

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

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

123 """ 

124 

125 @classmethod 

126 @abstractmethod 

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

128 """Construct an instance of the manager. 

129 

130 Parameters 

131 ---------- 

132 db : `Database` 

133 Interface to the underlying database engine and namespace. 

134 context : `StaticTablesContext` 

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

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

137 implemented with this manager. 

138 

139 Returns 

140 ------- 

141 manager : `OpaqueTableStorageManager` 

142 An instance of a concrete `OpaqueTableStorageManager` subclass. 

143 """ 

144 raise NotImplementedError() 

145 

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

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

148 `None` on failure. 

149 """ 

150 r = self.get(name) 

151 if r is None: 

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

153 return r 

154 

155 @abstractmethod 

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

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

158 an opaque logical table. 

159 

160 Parameters 

161 ---------- 

162 name : `str` 

163 Name of the logical table. 

164 

165 Returns 

166 ------- 

167 records : `OpaqueTableStorage` or `None` 

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

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

170 layer. 

171 

172 Notes 

173 ----- 

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

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

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

177 obtained from an existing data repository. 

178 """ 

179 raise NotImplementedError() 

180 

181 @abstractmethod 

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

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

184 table, creating new tables as necessary. 

185 

186 Parameters 

187 ---------- 

188 name : `str` 

189 Name of the logical table. 

190 spec : `TableSpec` 

191 Schema specification for the table to be created. 

192 

193 Returns 

194 ------- 

195 records : `OpaqueTableStorage` 

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

197 layer. 

198 

199 Notes 

200 ----- 

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

202 """ 

203 raise NotImplementedError()