Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/toml/encoder.py: 88%

197 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 07:03 +0000

1import datetime 

2import re 

3import sys 

4from decimal import Decimal 

5 

6from toml.decoder import InlineTableDict 

7 

8if sys.version_info >= (3,): 

9 unicode = str 

10 

11 

12def dump(o, f, encoder=None): 

13 """Writes out dict as toml to a file 

14 

15 Args: 

16 o: Object to dump into toml 

17 f: File descriptor where the toml should be stored 

18 encoder: The ``TomlEncoder`` to use for constructing the output string 

19 

20 Returns: 

21 String containing the toml corresponding to dictionary 

22 

23 Raises: 

24 TypeError: When anything other than file descriptor is passed 

25 """ 

26 

27 if not f.write: 

28 raise TypeError("You can only dump an object to a file descriptor") 

29 d = dumps(o, encoder=encoder) 

30 f.write(d) 

31 return d 

32 

33 

34def dumps(o, encoder=None): 

35 """Stringifies input dict as toml 

36 

37 Args: 

38 o: Object to dump into toml 

39 encoder: The ``TomlEncoder`` to use for constructing the output string 

40 

41 Returns: 

42 String containing the toml corresponding to dict 

43 

44 Examples: 

45 ```python 

46 >>> import toml 

47 >>> output = { 

48 ... 'a': "I'm a string", 

49 ... 'b': ["I'm", "a", "list"], 

50 ... 'c': 2400 

51 ... } 

52 >>> toml.dumps(output) 

53 'a = "I\'m a string"\nb = [ "I\'m", "a", "list",]\nc = 2400\n' 

54 ``` 

55 """ 

56 

57 retval = "" 

58 if encoder is None: 

59 encoder = TomlEncoder(o.__class__) 

60 addtoretval, sections = encoder.dump_sections(o, "") 

61 retval += addtoretval 

62 outer_objs = [id(o)] 

63 while sections: 

64 section_ids = [id(section) for section in sections.values()] 

65 for outer_obj in outer_objs: 

66 if outer_obj in section_ids: 

67 raise ValueError("Circular reference detected") 

68 outer_objs += section_ids 

69 newsections = encoder.get_empty_table() 

70 for section in sections: 

71 addtoretval, addtosections = encoder.dump_sections( 

72 sections[section], section) 

73 

74 if addtoretval or (not addtoretval and not addtosections): 

75 if retval and retval[-2:] != "\n\n": 

76 retval += "\n" 

77 retval += "[" + section + "]\n" 

78 if addtoretval: 

79 retval += addtoretval 

80 for s in addtosections: 

81 newsections[section + "." + s] = addtosections[s] 

82 sections = newsections 

83 return retval 

84 

85 

86def _dump_str(v): 

87 if sys.version_info < (3,) and hasattr(v, 'decode') and isinstance(v, str): 

88 v = v.decode('utf-8') 

89 v = "%r" % v 

90 if v[0] == 'u': 

91 v = v[1:] 

92 singlequote = v.startswith("'") 

93 if singlequote or v.startswith('"'): 

94 v = v[1:-1] 

95 if singlequote: 

96 v = v.replace("\\'", "'") 

97 v = v.replace('"', '\\"') 

98 v = v.split("\\x") 

99 while len(v) > 1: 

100 i = -1 

101 if not v[0]: 

102 v = v[1:] 

103 v[0] = v[0].replace("\\\\", "\\") 

104 # No, I don't know why != works and == breaks 

105 joinx = v[0][i] != "\\" 

106 while v[0][:i] and v[0][i] == "\\": 

107 joinx = not joinx 

108 i -= 1 

109 if joinx: 

110 joiner = "x" 

111 else: 

112 joiner = "u00" 

113 v = [v[0] + joiner + v[1]] + v[2:] 

114 return unicode('"' + v[0] + '"') 

115 

116 

117def _dump_float(v): 

118 return "{}".format(v).replace("e+0", "e+").replace("e-0", "e-") 

119 

120 

121def _dump_time(v): 

122 utcoffset = v.utcoffset() 

123 if utcoffset is None: 

124 return v.isoformat() 

125 # The TOML norm specifies that it's local time thus we drop the offset 

126 return v.isoformat()[:-6] 

127 

128 

129class TomlEncoder(object): 

130 

131 def __init__(self, _dict=dict, preserve=False): 

132 self._dict = _dict 

133 self.preserve = preserve 

134 self.dump_funcs = { 

135 str: _dump_str, 

136 unicode: _dump_str, 

137 list: self.dump_list, 

138 bool: lambda v: unicode(v).lower(), 

139 int: lambda v: v, 

140 float: _dump_float, 

141 Decimal: _dump_float, 

142 datetime.datetime: lambda v: v.isoformat().replace('+00:00', 'Z'), 

143 datetime.time: _dump_time, 

144 datetime.date: lambda v: v.isoformat() 

145 } 

146 

147 def get_empty_table(self): 

148 return self._dict() 

149 

150 def dump_list(self, v): 

151 retval = "[" 

152 for u in v: 

153 retval += " " + unicode(self.dump_value(u)) + "," 

154 retval += "]" 

155 return retval 

156 

157 def dump_inline_table(self, section): 

158 """Preserve inline table in its compact syntax instead of expanding 

159 into subsection. 

160 

161 https://github.com/toml-lang/toml#user-content-inline-table 

162 """ 

163 retval = "" 

164 if isinstance(section, dict): 

165 val_list = [] 

166 for k, v in section.items(): 

167 val = self.dump_inline_table(v) 

168 val_list.append(k + " = " + val) 

169 retval += "{ " + ", ".join(val_list) + " }\n" 

170 return retval 

171 else: 

172 return unicode(self.dump_value(section)) 

173 

174 def dump_value(self, v): 

175 # Lookup function corresponding to v's type 

176 dump_fn = self.dump_funcs.get(type(v)) 

177 if dump_fn is None and hasattr(v, '__iter__'): 

178 dump_fn = self.dump_funcs[list] 

179 # Evaluate function (if it exists) else return v 

180 return dump_fn(v) if dump_fn is not None else self.dump_funcs[str](v) 

181 

182 def dump_sections(self, o, sup): 

183 retstr = "" 

184 if sup != "" and sup[-1] != ".": 

185 sup += '.' 

186 retdict = self._dict() 

187 arraystr = "" 

188 for section in o: 

189 section = unicode(section) 

190 qsection = section 

191 if not re.match(r'^[A-Za-z0-9_-]+$', section): 

192 qsection = _dump_str(section) 

193 if not isinstance(o[section], dict): 

194 arrayoftables = False 

195 if isinstance(o[section], list): 

196 for a in o[section]: 

197 if isinstance(a, dict): 

198 arrayoftables = True 

199 if arrayoftables: 

200 for a in o[section]: 

201 arraytabstr = "\n" 

202 arraystr += "[[" + sup + qsection + "]]\n" 

203 s, d = self.dump_sections(a, sup + qsection) 

204 if s: 

205 if s[0] == "[": 

206 arraytabstr += s 

207 else: 

208 arraystr += s 

209 while d: 

210 newd = self._dict() 

211 for dsec in d: 

212 s1, d1 = self.dump_sections(d[dsec], sup + 

213 qsection + "." + 

214 dsec) 

215 if s1: 

216 arraytabstr += ("[" + sup + qsection + 

217 "." + dsec + "]\n") 

218 arraytabstr += s1 

219 for s1 in d1: 

220 newd[dsec + "." + s1] = d1[s1] 

221 d = newd 

222 arraystr += arraytabstr 

223 else: 

224 if o[section] is not None: 

225 retstr += (qsection + " = " + 

226 unicode(self.dump_value(o[section])) + '\n') 

227 elif self.preserve and isinstance(o[section], InlineTableDict): 

228 retstr += (qsection + " = " + 

229 self.dump_inline_table(o[section])) 

230 else: 

231 retdict[qsection] = o[section] 

232 retstr += arraystr 

233 return (retstr, retdict) 

234 

235 

236class TomlPreserveInlineDictEncoder(TomlEncoder): 

237 

238 def __init__(self, _dict=dict): 

239 super(TomlPreserveInlineDictEncoder, self).__init__(_dict, True) 

240 

241 

242class TomlArraySeparatorEncoder(TomlEncoder): 

243 

244 def __init__(self, _dict=dict, preserve=False, separator=","): 

245 super(TomlArraySeparatorEncoder, self).__init__(_dict, preserve) 

246 if separator.strip() == "": 

247 separator = "," + separator 

248 elif separator.strip(' \t\n\r,'): 

249 raise ValueError("Invalid separator for arrays") 

250 self.separator = separator 

251 

252 def dump_list(self, v): 

253 t = [] 

254 retval = "[" 

255 for u in v: 

256 t.append(self.dump_value(u)) 

257 while t != []: 

258 s = [] 

259 for u in t: 

260 if isinstance(u, list): 

261 for r in u: 

262 s.append(r) 

263 else: 

264 retval += " " + unicode(u) + self.separator 

265 t = s 

266 retval += "]" 

267 return retval 

268 

269 

270class TomlNumpyEncoder(TomlEncoder): 

271 

272 def __init__(self, _dict=dict, preserve=False): 

273 import numpy as np 

274 super(TomlNumpyEncoder, self).__init__(_dict, preserve) 

275 self.dump_funcs[np.float16] = _dump_float 

276 self.dump_funcs[np.float32] = _dump_float 

277 self.dump_funcs[np.float64] = _dump_float 

278 self.dump_funcs[np.int16] = self._dump_int 

279 self.dump_funcs[np.int32] = self._dump_int 

280 self.dump_funcs[np.int64] = self._dump_int 

281 

282 def _dump_int(self, v): 

283 return "{}".format(int(v)) 

284 

285 

286class TomlPreserveCommentEncoder(TomlEncoder): 

287 

288 def __init__(self, _dict=dict, preserve=False): 

289 from toml.decoder import CommentValue 

290 super(TomlPreserveCommentEncoder, self).__init__(_dict, preserve) 

291 self.dump_funcs[CommentValue] = lambda v: v.dump(self.dump_value) 

292 

293 

294class TomlPathlibEncoder(TomlEncoder): 

295 

296 def _dump_pathlib_path(self, v): 

297 return _dump_str(str(v)) 

298 

299 def dump_value(self, v): 

300 if (3, 4) <= sys.version_info: 

301 import pathlib 

302 if isinstance(v, pathlib.PurePath): 

303 v = str(v) 

304 return super(TomlPathlibEncoder, self).dump_value(v)