Coverage for python/lsst/verify/blob.py: 31%

62 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-10-01 01:59 -0700

1# This file is part of verify. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://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 <https://www.gnu.org/licenses/>. 

21__all__ = ['Blob'] 

22 

23import uuid 

24 

25from .jsonmixin import JsonSerializationMixin 

26from .datum import Datum 

27 

28 

29class Blob(JsonSerializationMixin): 

30 r"""Blob is a flexible container of data, as `lsst.verify.Datum` \s, that 

31 are serializable to JSON. 

32 

33 Parameters 

34 ---------- 

35 name : `str` 

36 Name of this type of blob. Blobs from one pipeline Job execution to 

37 another that share the same name generally share the same schema of 

38 `lsst.verify.Datum`\ s. 

39 datums : `dict` of `lsst.verify.Datum`-types, optional 

40 Keys are names of datums. Values are `~lsst.verify.Datum`\ -types. 

41 Each `~lsst.verify.Datum` can be later retrived from the Blob instance 

42 by key. 

43 """ 

44 

45 def __init__(self, name, **datums): 

46 # Internal read-only instance ID, access with the name attribute 

47 self._id = uuid.uuid4().hex 

48 

49 if not isinstance(name, str): 

50 message = 'Blob name {0!r} must be a string'.format(name) 

51 raise TypeError(message) 

52 self._name = name 

53 

54 # Internal Datum dictionary 

55 self._datums = {} 

56 

57 for key, datum in datums.items(): 

58 self[key] = datum 

59 

60 @property 

61 def name(self): 

62 """Name of this blob (`str`).""" 

63 return self._name 

64 

65 @property 

66 def identifier(self): 

67 """Unique UUID4-based identifier for this blob (`str`).""" 

68 return self._id 

69 

70 @classmethod 

71 def deserialize(cls, identifier=None, name=None, data=None): 

72 """Deserialize fields from a blob JSON object into a `Blob` instance. 

73 

74 Parameters 

75 ---------- 

76 identifier : `str` 

77 Blob identifier. 

78 name : `str` 

79 Name of the blob type. 

80 data : `dict` 

81 Dictionary of named ``name: datum object`` key-value pairs. 

82 

83 Returns 

84 ------- 

85 blob : `Blob` 

86 The `Blob` instance deserialied from a blob JSON object. 

87 

88 Examples 

89 -------- 

90 This class method is designed to roundtrip JSON objects created a 

91 Blob instance. For example: 

92 

93 >>> import astropy.units as u 

94 >>> blob = Blob('demo') 

95 >>> blob['a_mag'] = Datum(28 * u.mag, label='i') 

96 >>> json_data = blob.json 

97 >>> new_blob = Blob.deserialize(**json_data) 

98 """ 

99 datums = {} 

100 if data is not None: 

101 for datum_key, datum_doc in data.items(): 

102 datum = Datum.deserialize(**datum_doc) 

103 datums[datum_key] = datum 

104 instance = cls(name, **datums) 

105 instance._id = identifier 

106 return instance 

107 

108 @property 

109 def json(self): 

110 """Job data as a JSON-serializable `dict`.""" 

111 json_doc = JsonSerializationMixin.jsonify_dict({ 

112 'identifier': self.identifier, 

113 'name': self.name, 

114 'data': self._datums}) 

115 return json_doc 

116 

117 def __setitem__(self, key, value): 

118 if not isinstance(key, str): 

119 message = 'Key {0!r} is not a string.'.format(key) 

120 raise KeyError(message) 

121 

122 if not isinstance(value, Datum): 

123 message = '{0} is not a Datum-type'.format(value) 

124 raise TypeError(message) 

125 

126 self._datums[key] = value 

127 

128 def __getitem__(self, key): 

129 return self._datums[key] 

130 

131 def __delitem__(self, key): 

132 del self._datums[key] 

133 

134 def __len__(self): 

135 return len(self._datums) 

136 

137 def __contains__(self, key): 

138 return key in self._datums 

139 

140 def __iter__(self): 

141 for key in self._datums: 

142 yield key 

143 

144 def __eq__(self, other): 

145 return (self.identifier == other.identifier) \ 

146 and (self.name == other.name) \ 

147 and (self._datums == other._datums) 

148 

149 def __ne__(self, other): 

150 return not self.__eq__(other) 

151 

152 def keys(self): 

153 """Get keys of blob items. 

154 

155 Returns 

156 ------- 

157 keys : sequence of `str` 

158 Sequence of keys to items in the Blob. 

159 """ 

160 return self._datums.keys() 

161 

162 def items(self): 

163 """Get pairs of keys and values in the Blob. 

164 

165 Yields 

166 ------ 

167 keyval : tuple 

168 Tuple of: 

169 

170 - key (`str`) 

171 - datum (`lsst.verify.Datum`) 

172 """ 

173 for key, val in self._datums.items(): 

174 yield key, val