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

28 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-07-15 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/>. 

21from __future__ import annotations 

22 

23__all__ = [ 

24 "ButlerAttributeManager", 

25 "ButlerAttributeExistsError", 

26] 

27 

28from abc import abstractmethod 

29from typing import TYPE_CHECKING, Iterable, Optional, Tuple 

30 

31from ._versioning import VersionedExtension 

32 

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

34 from ._database import Database, StaticTablesContext 

35 

36 

37class ButlerAttributeExistsError(RuntimeError): 

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

39 specifying ``force`` option. 

40 """ 

41 

42 

43class ButlerAttributeManager(VersionedExtension): 

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

45 

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

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

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

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

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

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

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

53 

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

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

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

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

58 database table with a stable schema. 

59 """ 

60 

61 @classmethod 

62 @abstractmethod 

63 def initialize(cls, db: Database, context: StaticTablesContext) -> ButlerAttributeManager: 

64 """Construct an instance of the manager. 

65 

66 Parameters 

67 ---------- 

68 db : `Database` 

69 Interface to the underlying database engine and namespace. 

70 context : `StaticTablesContext` 

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

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

73 implemented with this manager. 

74 

75 Returns 

76 ------- 

77 manager : `ButlerAttributeManager` 

78 An instance of `ButlerAttributeManager`. 

79 """ 

80 raise NotImplementedError() 

81 

82 @abstractmethod 

83 def get(self, name: str, default: Optional[str] = None) -> Optional[str]: 

84 """Retrieve value of a given attribute. 

85 

86 Parameters 

87 ---------- 

88 name : `str` 

89 Attribute name, arbitrary non-empty string. 

90 default : `str`, optional 

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

92 string or `None`. 

93 

94 Returns 

95 ------- 

96 value : `str` 

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

98 returned. 

99 """ 

100 raise NotImplementedError() 

101 

102 @abstractmethod 

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

104 """Set value for a given attribute. 

105 

106 Parameters 

107 ---------- 

108 name : `str` 

109 Attribute name, arbitrary non-empty string. 

110 value : `str` 

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

112 deficiencies of some database engines we are not allowing empty 

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

114 empty string. 

115 force : `bool`, optional 

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

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

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

119 updated. 

120 

121 Raises 

122 ------ 

123 ButlerAttributeExistsError 

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

125 ValueError 

126 Raised if name or value parameters are empty. 

127 """ 

128 raise NotImplementedError() 

129 

130 @abstractmethod 

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

132 """Delete an attribute. 

133 

134 Parameters 

135 ---------- 

136 name : `str` 

137 Attribute name, arbitrary non-empty string. 

138 

139 Returns 

140 ------- 

141 existed : `bool` 

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

143 """ 

144 raise NotImplementedError() 

145 

146 @abstractmethod 

147 def items(self) -> Iterable[Tuple[str, str]]: 

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

149 

150 Yields 

151 ------ 

152 name : `str` 

153 Attribute name. 

154 value : `str` 

155 Corresponding attribute value. 

156 """ 

157 raise NotImplementedError() 

158 

159 @abstractmethod 

160 def empty(self) -> bool: 

161 """Check whether attributes set is empty. 

162 

163 Returns 

164 ------- 

165 empty : `bool` 

166 True if there are no any attributes defined. 

167 """ 

168 raise NotImplementedError()