Coverage for python/lsst/daf/butler/registry/interfaces/_attributes.py: 97%

23 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-10-02 08:00 +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 software is dual licensed under the GNU General Public License and also 

10# under a 3-clause BSD license. Recipients may choose which of these licenses 

11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt, 

12# respectively. If you choose the GPL option then the following text applies 

13# (but note that there is still no warranty even if you opt for BSD instead): 

14# 

15# This program is free software: you can redistribute it and/or modify 

16# it under the terms of the GNU General Public License as published by 

17# the Free Software Foundation, either version 3 of the License, or 

18# (at your option) any later version. 

19# 

20# This program is distributed in the hope that it will be useful, 

21# but WITHOUT ANY WARRANTY; without even the implied warranty of 

22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

23# GNU General Public License for more details. 

24# 

25# You should have received a copy of the GNU General Public License 

26# along with this program. If not, see <http://www.gnu.org/licenses/>. 

27from __future__ import annotations 

28 

29__all__ = [ 

30 "ButlerAttributeManager", 

31 "ButlerAttributeExistsError", 

32] 

33 

34from abc import abstractmethod 

35from collections.abc import Iterable 

36from typing import TYPE_CHECKING 

37 

38from ._versioning import VersionedExtension, VersionTuple 

39 

40if TYPE_CHECKING: 

41 from ._database import Database, StaticTablesContext 

42 

43 

44class ButlerAttributeExistsError(RuntimeError): 

45 """Exception raised when trying to update existing attribute without 

46 specifying ``force`` option. 

47 """ 

48 

49 

50class ButlerAttributeManager(VersionedExtension): 

51 """An interface for managing butler attributes in a `Registry`. 

52 

53 Attributes are represented in registry as a set of name-value pairs, both 

54 have string type. Any non-string data types (e.g. integers) need to be 

55 converted to/from strings on client side. Attribute names can be arbitrary 

56 strings, no particular structure is enforced by this interface. Attribute 

57 names are globally unique, to avoid potential collision clients should 

58 follow some common convention for attribute names, e.g. dot-separated 

59 components (``config.managers.opaque``). 

60 

61 One of the critical pieces of information that will be stored as 

62 attribute is the version of database schema which needs to be known 

63 before registry can do any operations on database. For that reasons it is 

64 likely there will be only one implementation of this interface which uses 

65 database table with a stable schema. 

66 """ 

67 

68 def __init__(self, *, registry_schema_version: VersionTuple | None = None) -> None: 

69 super().__init__(registry_schema_version=registry_schema_version) 

70 

71 @classmethod 

72 @abstractmethod 

73 def initialize( 

74 cls, db: Database, context: StaticTablesContext, registry_schema_version: VersionTuple | None = None 

75 ) -> ButlerAttributeManager: 

76 """Construct an instance of the manager. 

77 

78 Parameters 

79 ---------- 

80 db : `Database` 

81 Interface to the underlying database engine and namespace. 

82 context : `StaticTablesContext` 

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

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

85 implemented with this manager. 

86 registry_schema_version : `VersionTuple` or `None` 

87 Schema version of this extension as defined in registry. 

88 

89 Returns 

90 ------- 

91 manager : `ButlerAttributeManager` 

92 An instance of `ButlerAttributeManager`. 

93 """ 

94 raise NotImplementedError() 

95 

96 @abstractmethod 

97 def get(self, name: str, default: str | None = None) -> str | None: 

98 """Retrieve value of a given attribute. 

99 

100 Parameters 

101 ---------- 

102 name : `str` 

103 Attribute name, arbitrary non-empty string. 

104 default : `str`, optional 

105 Default value returned when attribute does not exist, can be 

106 string or `None`. 

107 

108 Returns 

109 ------- 

110 value : `str` 

111 Attribute value, if attribute does not exist then ``default`` is 

112 returned. 

113 """ 

114 raise NotImplementedError() 

115 

116 @abstractmethod 

117 def set(self, name: str, value: str, *, force: bool = False) -> None: 

118 """Set value for a given attribute. 

119 

120 Parameters 

121 ---------- 

122 name : `str` 

123 Attribute name, arbitrary non-empty string. 

124 value : `str` 

125 New value for an attribute, an arbitrary string. Due to 

126 deficiencies of some database engines we are not allowing empty 

127 strings to be stored in the database, and ``value`` cannot be an 

128 empty string. 

129 force : `bool`, optional 

130 Controls handling of existing attributes. With default `False` 

131 value an exception is raised if attribute ``name`` already exists, 

132 if `True` is passed then value of the existing attribute will be 

133 updated. 

134 

135 Raises 

136 ------ 

137 ButlerAttributeExistsError 

138 Raised if attribute already exists but ``force`` option is false. 

139 ValueError 

140 Raised if name or value parameters are empty. 

141 """ 

142 raise NotImplementedError() 

143 

144 @abstractmethod 

145 def delete(self, name: str) -> bool: 

146 """Delete an attribute. 

147 

148 Parameters 

149 ---------- 

150 name : `str` 

151 Attribute name, arbitrary non-empty string. 

152 

153 Returns 

154 ------- 

155 existed : `bool` 

156 `True` is returned if attribute existed before it was deleted. 

157 """ 

158 raise NotImplementedError() 

159 

160 @abstractmethod 

161 def items(self) -> Iterable[tuple[str, str]]: 

162 """Iterate over attributes and yield their names and values. 

163 

164 Yields 

165 ------ 

166 name : `str` 

167 Attribute name. 

168 value : `str` 

169 Corresponding attribute value. 

170 """ 

171 raise NotImplementedError() 

172 

173 @abstractmethod 

174 def empty(self) -> bool: 

175 """Check whether attributes set is empty. 

176 

177 Returns 

178 ------- 

179 empty : `bool` 

180 True if there are no any attributes defined. 

181 """ 

182 raise NotImplementedError()