Hide keyboard shortcuts

Hot-keys 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

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/>. 

21 

22from __future__ import annotations 

23 

24__all__ = ("YamlFormatter", ) 

25 

26import builtins 

27import yaml 

28 

29from typing import ( 

30 TYPE_CHECKING, 

31 Any, 

32 Optional, 

33 Type, 

34) 

35 

36from .file import FileFormatter 

37 

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

39 from lsst.daf.butler import StorageClass 

40 

41 

42class YamlFormatter(FileFormatter): 

43 """Interface for reading and writing Python objects to and from YAML files. 

44 """ 

45 extension = ".yaml" 

46 

47 unsupportedParameters = None 

48 """This formatter does not support any parameters""" 

49 

50 supportedWriteParameters = frozenset({"unsafe_dump"}) 

51 """Allow the normal yaml.dump to be used to write the YAML. Use this 

52 if you know that your class has registered representers.""" 

53 

54 def _readFile(self, path: str, pytype: Type[Any] = None) -> Any: 

55 """Read a file from the path in YAML format. 

56 

57 Parameters 

58 ---------- 

59 path : `str` 

60 Path to use to open YAML format file. 

61 pytype : `class`, optional 

62 Not used by this implementation. 

63 

64 Returns 

65 ------- 

66 data : `object` 

67 Either data as Python object read from YAML file, or None 

68 if the file could not be opened. 

69 

70 Notes 

71 ----- 

72 The `~yaml.SafeLoader` is used when parsing the YAML file. 

73 """ 

74 try: 

75 with open(path, "rb") as fd: 

76 data = self._fromBytes(fd.read(), pytype) 

77 except FileNotFoundError: 

78 data = None 

79 

80 return data 

81 

82 def _fromBytes(self, serializedDataset: bytes, pytype: Optional[Type[Any]] = None) -> Any: 

83 """Read the bytes object as a python object. 

84 

85 Parameters 

86 ---------- 

87 serializedDataset : `bytes` 

88 Bytes object to unserialize. 

89 pytype : `class`, optional 

90 Not used by this implementation. 

91 

92 Returns 

93 ------- 

94 inMemoryDataset : `object` 

95 The requested data as an object, or None if the string could 

96 not be read. 

97 

98 Notes 

99 ----- 

100 The `~yaml.SafeLoader` is used when parsing the YAML. 

101 """ 

102 data = yaml.safe_load(serializedDataset) 

103 

104 try: 

105 data = data.exportAsDict() 

106 except AttributeError: 

107 pass 

108 return data 

109 

110 def _writeFile(self, inMemoryDataset: Any) -> None: 

111 """Write the in memory dataset to file on disk. 

112 

113 Will look for `_asdict()` method to aid YAML serialization, following 

114 the approach of the simplejson module. The `dict` will be passed 

115 to the relevant constructor on read. 

116 

117 Parameters 

118 ---------- 

119 inMemoryDataset : `object` 

120 Object to serialize. 

121 

122 Raises 

123 ------ 

124 Exception 

125 The file could not be written. 

126 

127 Notes 

128 ----- 

129 The `~yaml.SafeDumper` is used when generating the YAML serialization. 

130 This will fail for data structures that have complex python classes 

131 without a registered YAML representer. 

132 """ 

133 with open(self.fileDescriptor.location.path, "wb") as fd: 

134 fd.write(self._toBytes(inMemoryDataset)) 

135 

136 def _toBytes(self, inMemoryDataset: Any) -> bytes: 

137 """Write the in memory dataset to a bytestring. 

138 

139 Will look for `_asdict()` method to aid YAML serialization, following 

140 the approach of the simplejson module. The `dict` will be passed 

141 to the relevant constructor on read. 

142 

143 Parameters 

144 ---------- 

145 inMemoryDataset : `object` 

146 Object to serialize 

147 

148 Returns 

149 ------- 

150 serializedDataset : `bytes` 

151 YAML string encoded to bytes. 

152 

153 Raises 

154 ------ 

155 Exception 

156 The object could not be serialized. 

157 

158 Notes 

159 ----- 

160 The `~yaml.SafeDumper` is used when generating the YAML serialization. 

161 This will fail for data structures that have complex python classes 

162 without a registered YAML representer. 

163 """ 

164 if hasattr(inMemoryDataset, "_asdict"): 

165 inMemoryDataset = inMemoryDataset._asdict() 

166 unsafe_dump = self.writeParameters.get("unsafe_dump", False) 

167 if unsafe_dump: 

168 serialized = yaml.dump(inMemoryDataset) 

169 else: 

170 serialized = yaml.safe_dump(inMemoryDataset) 

171 return serialized.encode() 

172 

173 def _coerceType(self, inMemoryDataset: Any, storageClass: StorageClass, 

174 pytype: Optional[Type[Any]] = None) -> Any: 

175 """Coerce the supplied inMemoryDataset to type `pytype`. 

176 

177 Parameters 

178 ---------- 

179 inMemoryDataset : `object` 

180 Object to coerce to expected type. 

181 storageClass : `StorageClass` 

182 StorageClass associated with `inMemoryDataset`. 

183 pytype : `type`, optional 

184 Override type to use for conversion. 

185 

186 Returns 

187 ------- 

188 inMemoryDataset : `object` 

189 Object of expected type `pytype`. 

190 """ 

191 if inMemoryDataset is not None and pytype is not None and not hasattr(builtins, pytype.__name__): 

192 if storageClass.isComposite(): 

193 inMemoryDataset = storageClass.delegate().assemble(inMemoryDataset, pytype=pytype) 

194 elif not isinstance(inMemoryDataset, pytype): 

195 # Hope that we can pass the arguments in directly 

196 inMemoryDataset = pytype(inMemoryDataset) 

197 return inMemoryDataset