Codebase list pytoml / fresh-snapshots/main
[ Debian Janitor ] New upstream snapshot. Debian Janitor 2 years ago
17 changed file(s) with 996 addition(s) and 987 deletion(s). Raw diff Collapse all Expand all
0 No-notice MIT License
1
2 Permission is hereby granted, free of charge, to any person obtaining a copy
3 of this software and associated documentation files (the "Software"), to deal
4 in the Software without restriction, including without limitation the rights
5 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
6 copies of the Software, and to permit persons to whom the Software is
7 furnished to do so.
8
9 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
12 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
13 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
14 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
15 THE SOFTWARE.
0 No-notice MIT License
1
2 Permission is hereby granted, free of charge, to any person obtaining a copy
3 of this software and associated documentation files (the "Software"), to deal
4 in the Software without restriction, including without limitation the rights
5 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
6 copies of the Software, and to permit persons to whom the Software is
7 furnished to do so.
8
9 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
12 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
13 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
14 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
15 THE SOFTWARE.
0 include LICENSE
1 include README.md
0 include LICENSE
1 include README.md
0 Metadata-Version: 2.1
1 Name: pytoml
2 Version: 0.1.21
3 Summary: A parser for TOML-0.4.0
4 Home-page: https://github.com/avakar/pytoml
5 Author: Martin Vejnár
6 Author-email: vejnar.martin@gmail.com
7 License: MIT
8 Description: [![PyPI](https://img.shields.io/pypi/v/pytoml.svg)](https://pypi.python.org/pypi/pytoml)
9 [![Build Status](https://travis-ci.org/avakar/pytoml.svg?branch=master)](https://travis-ci.org/avakar/pytoml)
10
11 # Deprecated
12
13 The pytoml project is no longer being actively maintained. Consider using the
14 [toml](https://github.com/uiri/toml) package instead.
15
16 # pytoml
17
18 This project aims at being a specs-conforming and strict parser and writer for [TOML][1] files.
19 The library currently supports [version 0.4.0][2] of the specs and runs with Python 2.7+ and 3.5+.
20
21 Install:
22
23 pip install pytoml
24
25 The interface is the same as for the standard `json` package.
26
27 >>> import pytoml as toml
28 >>> toml.loads('a = 1')
29 {'a': 1}
30 >>> with open('file.toml', 'rb') as fin:
31 ... obj = toml.load(fin)
32 >>> obj
33 {'a': 1}
34
35 The `loads` function accepts either a bytes object
36 (that gets decoded as UTF-8 with no BOM allowed),
37 or a unicode object.
38
39 Use `dump` or `dumps` to serialize a dict into TOML.
40
41 >>> print toml.dumps(obj)
42 a = 1
43
44 ## tests
45
46 To run the tests update the `toml-test` submodule:
47
48 git submodule update --init --recursive
49
50 Then run the tests:
51
52 python test/test.py
53
54 [1]: https://github.com/toml-lang/toml
55 [2]: https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md
56
57 Platform: UNKNOWN
58 Classifier: Programming Language :: Python :: 2
59 Classifier: Programming Language :: Python :: 2.7
60 Classifier: Programming Language :: Python :: 3
61 Classifier: Programming Language :: Python :: 3.5
62 Classifier: Programming Language :: Python :: 3.6
63 Classifier: Programming Language :: Python :: 3.7
64 Classifier: License :: OSI Approved :: MIT License
65 Classifier: Topic :: Software Development :: Libraries
66 Description-Content-Type: text/markdown
0 Metadata-Version: 2.1
1 Name: pytoml
2 Version: 0.1.21
3 Summary: A parser for TOML-0.4.0
4 Home-page: https://github.com/avakar/pytoml
5 Author: Martin Vejnár
6 Author-email: vejnar.martin@gmail.com
7 License: MIT
8 Platform: UNKNOWN
9 Classifier: Programming Language :: Python :: 2
10 Classifier: Programming Language :: Python :: 2.7
11 Classifier: Programming Language :: Python :: 3
12 Classifier: Programming Language :: Python :: 3.5
13 Classifier: Programming Language :: Python :: 3.6
14 Classifier: Programming Language :: Python :: 3.7
15 Classifier: License :: OSI Approved :: MIT License
16 Classifier: Topic :: Software Development :: Libraries
17 Description-Content-Type: text/markdown
18 License-File: LICENSE
19
20 [![PyPI](https://img.shields.io/pypi/v/pytoml.svg)](https://pypi.python.org/pypi/pytoml)
21 [![Build Status](https://travis-ci.org/avakar/pytoml.svg?branch=master)](https://travis-ci.org/avakar/pytoml)
22
23 # Deprecated
24
25 The pytoml project is no longer being actively maintained. Consider using the
26 [toml](https://github.com/uiri/toml) package instead.
27
28 # pytoml
29
30 This project aims at being a specs-conforming and strict parser and writer for [TOML][1] files.
31 The library currently supports [version 0.4.0][2] of the specs and runs with Python 2.7+ and 3.5+.
32
33 Install:
34
35 pip install pytoml
36
37 The interface is the same as for the standard `json` package.
38
39 >>> import pytoml as toml
40 >>> toml.loads('a = 1')
41 {'a': 1}
42 >>> with open('file.toml', 'rb') as fin:
43 ... obj = toml.load(fin)
44 >>> obj
45 {'a': 1}
46
47 The `loads` function accepts either a bytes object
48 (that gets decoded as UTF-8 with no BOM allowed),
49 or a unicode object.
50
51 Use `dump` or `dumps` to serialize a dict into TOML.
52
53 >>> print toml.dumps(obj)
54 a = 1
55
56 ## tests
57
58 To run the tests update the `toml-test` submodule:
59
60 git submodule update --init --recursive
61
62 Then run the tests:
63
64 python test/test.py
65
66 [1]: https://github.com/toml-lang/toml
67 [2]: https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md
68
69
0 [![PyPI](https://img.shields.io/pypi/v/pytoml.svg)](https://pypi.python.org/pypi/pytoml)
1 [![Build Status](https://travis-ci.org/avakar/pytoml.svg?branch=master)](https://travis-ci.org/avakar/pytoml)
2
3 # Deprecated
4
5 The pytoml project is no longer being actively maintained. Consider using the
6 [toml](https://github.com/uiri/toml) package instead.
7
8 # pytoml
9
10 This project aims at being a specs-conforming and strict parser and writer for [TOML][1] files.
11 The library currently supports [version 0.4.0][2] of the specs and runs with Python 2.7+ and 3.5+.
12
13 Install:
14
15 pip install pytoml
16
17 The interface is the same as for the standard `json` package.
18
19 >>> import pytoml as toml
20 >>> toml.loads('a = 1')
21 {'a': 1}
22 >>> with open('file.toml', 'rb') as fin:
23 ... obj = toml.load(fin)
24 >>> obj
25 {'a': 1}
26
27 The `loads` function accepts either a bytes object
28 (that gets decoded as UTF-8 with no BOM allowed),
29 or a unicode object.
30
31 Use `dump` or `dumps` to serialize a dict into TOML.
32
33 >>> print toml.dumps(obj)
34 a = 1
35
36 ## tests
37
38 To run the tests update the `toml-test` submodule:
39
40 git submodule update --init --recursive
41
42 Then run the tests:
43
44 python test/test.py
45
46 [1]: https://github.com/toml-lang/toml
47 [2]: https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md
0 [![PyPI](https://img.shields.io/pypi/v/pytoml.svg)](https://pypi.python.org/pypi/pytoml)
1 [![Build Status](https://travis-ci.org/avakar/pytoml.svg?branch=master)](https://travis-ci.org/avakar/pytoml)
2
3 # Deprecated
4
5 The pytoml project is no longer being actively maintained. Consider using the
6 [toml](https://github.com/uiri/toml) package instead.
7
8 # pytoml
9
10 This project aims at being a specs-conforming and strict parser and writer for [TOML][1] files.
11 The library currently supports [version 0.4.0][2] of the specs and runs with Python 2.7+ and 3.5+.
12
13 Install:
14
15 pip install pytoml
16
17 The interface is the same as for the standard `json` package.
18
19 >>> import pytoml as toml
20 >>> toml.loads('a = 1')
21 {'a': 1}
22 >>> with open('file.toml', 'rb') as fin:
23 ... obj = toml.load(fin)
24 >>> obj
25 {'a': 1}
26
27 The `loads` function accepts either a bytes object
28 (that gets decoded as UTF-8 with no BOM allowed),
29 or a unicode object.
30
31 Use `dump` or `dumps` to serialize a dict into TOML.
32
33 >>> print toml.dumps(obj)
34 a = 1
35
36 ## tests
37
38 To run the tests update the `toml-test` submodule:
39
40 git submodule update --init --recursive
41
42 Then run the tests:
43
44 python test/test.py
45
46 [1]: https://github.com/toml-lang/toml
47 [2]: https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md
0 pytoml (0.1.21-2) UNRELEASED; urgency=medium
0 pytoml (0.1.21+git20190721.1.cd54e91-1) UNRELEASED; urgency=medium
11
22 [ Ondřej Nový ]
33 * d/control: Update Vcs-* fields with new Debian Python Team Salsa
66 [ Sandro Tosi ]
77 * Use the new Debian Python Team contact name and address
88
9 -- Sandro Tosi <morph@debian.org> Mon, 04 Jan 2021 17:07:48 -0500
9 [ Debian Janitor ]
10 * New upstream snapshot.
11
12 -- Sandro Tosi <morph@debian.org> Sun, 07 Nov 2021 14:25:53 -0000
1013
1114 pytoml (0.1.21-1) unstable; urgency=medium
1215
0 from .core import TomlError
1 from .parser import load, loads
2 from .test import translate_to_test
0 from .core import TomlError
1 from .parser import load, loads
2 from .test import translate_to_test
33 from .writer import dump, dumps
0 class TomlError(RuntimeError):
1 def __init__(self, message, line, col, filename):
2 RuntimeError.__init__(self, message, line, col, filename)
3 self.message = message
4 self.line = line
5 self.col = col
6 self.filename = filename
7
8 def __str__(self):
9 return '{}({}, {}): {}'.format(self.filename, self.line, self.col, self.message)
10
11 def __repr__(self):
12 return 'TomlError({!r}, {!r}, {!r}, {!r})'.format(self.message, self.line, self.col, self.filename)
0 class TomlError(RuntimeError):
1 def __init__(self, message, line, col, filename):
2 RuntimeError.__init__(self, message, line, col, filename)
3 self.message = message
4 self.line = line
5 self.col = col
6 self.filename = filename
7
8 def __str__(self):
9 return '{}({}, {}): {}'.format(self.filename, self.line, self.col, self.message)
10
11 def __repr__(self):
12 return 'TomlError({!r}, {!r}, {!r}, {!r})'.format(self.message, self.line, self.col, self.filename)
0 import re, sys
1 from .core import TomlError
2 from .utils import rfc3339_re, parse_rfc3339_re
3
4 if sys.version_info[0] == 2:
5 _chr = unichr
6 else:
7 _chr = chr
8
9 def load(fin, translate=lambda t, x, v: v, object_pairs_hook=dict):
10 return loads(fin.read(), translate=translate, object_pairs_hook=object_pairs_hook, filename=getattr(fin, 'name', repr(fin)))
11
12 def loads(s, filename='<string>', translate=lambda t, x, v: v, object_pairs_hook=dict):
13 if isinstance(s, bytes):
14 s = s.decode('utf-8')
15
16 s = s.replace('\r\n', '\n')
17
18 root = object_pairs_hook()
19 tables = object_pairs_hook()
20 scope = root
21
22 src = _Source(s, filename=filename)
23 ast = _p_toml(src, object_pairs_hook=object_pairs_hook)
24
25 def error(msg):
26 raise TomlError(msg, pos[0], pos[1], filename)
27
28 def process_value(v, object_pairs_hook):
29 kind, text, value, pos = v
30 if kind == 'array':
31 if value and any(k != value[0][0] for k, t, v, p in value[1:]):
32 error('array-type-mismatch')
33 value = [process_value(item, object_pairs_hook=object_pairs_hook) for item in value]
34 elif kind == 'table':
35 value = object_pairs_hook([(k, process_value(value[k], object_pairs_hook=object_pairs_hook)) for k in value])
36 return translate(kind, text, value)
37
38 for kind, value, pos in ast:
39 if kind == 'kv':
40 k, v = value
41 if k in scope:
42 error('duplicate_keys. Key "{0}" was used more than once.'.format(k))
43 scope[k] = process_value(v, object_pairs_hook=object_pairs_hook)
44 else:
45 is_table_array = (kind == 'table_array')
46 cur = tables
47 for name in value[:-1]:
48 if isinstance(cur.get(name), list):
49 d, cur = cur[name][-1]
50 else:
51 d, cur = cur.setdefault(name, (None, object_pairs_hook()))
52
53 scope = object_pairs_hook()
54 name = value[-1]
55 if name not in cur:
56 if is_table_array:
57 cur[name] = [(scope, object_pairs_hook())]
58 else:
59 cur[name] = (scope, object_pairs_hook())
60 elif isinstance(cur[name], list):
61 if not is_table_array:
62 error('table_type_mismatch')
63 cur[name].append((scope, object_pairs_hook()))
64 else:
65 if is_table_array:
66 error('table_type_mismatch')
67 old_scope, next_table = cur[name]
68 if old_scope is not None:
69 error('duplicate_tables')
70 cur[name] = (scope, next_table)
71
72 def merge_tables(scope, tables):
73 if scope is None:
74 scope = object_pairs_hook()
75 for k in tables:
76 if k in scope:
77 error('key_table_conflict')
78 v = tables[k]
79 if isinstance(v, list):
80 scope[k] = [merge_tables(sc, tbl) for sc, tbl in v]
81 else:
82 scope[k] = merge_tables(v[0], v[1])
83 return scope
84
85 return merge_tables(root, tables)
86
87 class _Source:
88 def __init__(self, s, filename=None):
89 self.s = s
90 self._pos = (1, 1)
91 self._last = None
92 self._filename = filename
93 self.backtrack_stack = []
94
95 def last(self):
96 return self._last
97
98 def pos(self):
99 return self._pos
100
101 def fail(self):
102 return self._expect(None)
103
104 def consume_dot(self):
105 if self.s:
106 self._last = self.s[0]
107 self.s = self[1:]
108 self._advance(self._last)
109 return self._last
110 return None
111
112 def expect_dot(self):
113 return self._expect(self.consume_dot())
114
115 def consume_eof(self):
116 if not self.s:
117 self._last = ''
118 return True
119 return False
120
121 def expect_eof(self):
122 return self._expect(self.consume_eof())
123
124 def consume(self, s):
125 if self.s.startswith(s):
126 self.s = self.s[len(s):]
127 self._last = s
128 self._advance(s)
129 return True
130 return False
131
132 def expect(self, s):
133 return self._expect(self.consume(s))
134
135 def consume_re(self, re):
136 m = re.match(self.s)
137 if m:
138 self.s = self.s[len(m.group(0)):]
139 self._last = m
140 self._advance(m.group(0))
141 return m
142 return None
143
144 def expect_re(self, re):
145 return self._expect(self.consume_re(re))
146
147 def __enter__(self):
148 self.backtrack_stack.append((self.s, self._pos))
149
150 def __exit__(self, type, value, traceback):
151 if type is None:
152 self.backtrack_stack.pop()
153 else:
154 self.s, self._pos = self.backtrack_stack.pop()
155 return type == TomlError
156
157 def commit(self):
158 self.backtrack_stack[-1] = (self.s, self._pos)
159
160 def _expect(self, r):
161 if not r:
162 raise TomlError('msg', self._pos[0], self._pos[1], self._filename)
163 return r
164
165 def _advance(self, s):
166 suffix_pos = s.rfind('\n')
167 if suffix_pos == -1:
168 self._pos = (self._pos[0], self._pos[1] + len(s))
169 else:
170 self._pos = (self._pos[0] + s.count('\n'), len(s) - suffix_pos)
171
172 _ews_re = re.compile(r'(?:[ \t]|#[^\n]*\n|#[^\n]*\Z|\n)*')
173 def _p_ews(s):
174 s.expect_re(_ews_re)
175
176 _ws_re = re.compile(r'[ \t]*')
177 def _p_ws(s):
178 s.expect_re(_ws_re)
179
180 _escapes = { 'b': '\b', 'n': '\n', 'r': '\r', 't': '\t', '"': '"',
181 '\\': '\\', 'f': '\f' }
182
183 _basicstr_re = re.compile(r'[^"\\\000-\037]*')
184 _short_uni_re = re.compile(r'u([0-9a-fA-F]{4})')
185 _long_uni_re = re.compile(r'U([0-9a-fA-F]{8})')
186 _escapes_re = re.compile(r'[btnfr\"\\]')
187 _newline_esc_re = re.compile('\n[ \t\n]*')
188 def _p_basicstr_content(s, content=_basicstr_re):
189 res = []
190 while True:
191 res.append(s.expect_re(content).group(0))
192 if not s.consume('\\'):
193 break
194 if s.consume_re(_newline_esc_re):
195 pass
196 elif s.consume_re(_short_uni_re) or s.consume_re(_long_uni_re):
197 v = int(s.last().group(1), 16)
198 if 0xd800 <= v < 0xe000:
199 s.fail()
200 res.append(_chr(v))
201 else:
202 s.expect_re(_escapes_re)
203 res.append(_escapes[s.last().group(0)])
204 return ''.join(res)
205
206 _key_re = re.compile(r'[0-9a-zA-Z-_]+')
207 def _p_key(s):
208 with s:
209 s.expect('"')
210 r = _p_basicstr_content(s, _basicstr_re)
211 s.expect('"')
212 return r
213 if s.consume('\''):
214 if s.consume('\'\''):
215 s.consume('\n')
216 r = s.expect_re(_litstr_ml_re).group(0)
217 s.expect('\'\'\'')
218 else:
219 r = s.expect_re(_litstr_re).group(0)
220 s.expect('\'')
221 return r
222 return s.expect_re(_key_re).group(0)
223
224 _float_re = re.compile(r'[+-]?(?:0|[1-9](?:_?\d)*)(?:\.\d(?:_?\d)*)?(?:[eE][+-]?(?:\d(?:_?\d)*))?')
225
226 _basicstr_ml_re = re.compile(r'(?:""?(?!")|[^"\\\000-\011\013-\037])*')
227 _litstr_re = re.compile(r"[^'\000\010\012-\037]*")
228 _litstr_ml_re = re.compile(r"(?:(?:|'|'')(?:[^'\000-\010\013-\037]))*")
229 def _p_value(s, object_pairs_hook):
230 pos = s.pos()
231
232 if s.consume('true'):
233 return 'bool', s.last(), True, pos
234 if s.consume('false'):
235 return 'bool', s.last(), False, pos
236
237 if s.consume('"'):
238 if s.consume('""'):
239 s.consume('\n')
240 r = _p_basicstr_content(s, _basicstr_ml_re)
241 s.expect('"""')
242 else:
243 r = _p_basicstr_content(s, _basicstr_re)
244 s.expect('"')
245 return 'str', r, r, pos
246
247 if s.consume('\''):
248 if s.consume('\'\''):
249 s.consume('\n')
250 r = s.expect_re(_litstr_ml_re).group(0)
251 s.expect('\'\'\'')
252 else:
253 r = s.expect_re(_litstr_re).group(0)
254 s.expect('\'')
255 return 'str', r, r, pos
256
257 if s.consume_re(rfc3339_re):
258 m = s.last()
259 return 'datetime', m.group(0), parse_rfc3339_re(m), pos
260
261 if s.consume_re(_float_re):
262 m = s.last().group(0)
263 r = m.replace('_','')
264 if '.' in m or 'e' in m or 'E' in m:
265 return 'float', m, float(r), pos
266 else:
267 return 'int', m, int(r, 10), pos
268
269 if s.consume('['):
270 items = []
271 with s:
272 while True:
273 _p_ews(s)
274 items.append(_p_value(s, object_pairs_hook=object_pairs_hook))
275 s.commit()
276 _p_ews(s)
277 s.expect(',')
278 s.commit()
279 _p_ews(s)
280 s.expect(']')
281 return 'array', None, items, pos
282
283 if s.consume('{'):
284 _p_ws(s)
285 items = object_pairs_hook()
286 if not s.consume('}'):
287 k = _p_key(s)
288 _p_ws(s)
289 s.expect('=')
290 _p_ws(s)
291 items[k] = _p_value(s, object_pairs_hook=object_pairs_hook)
292 _p_ws(s)
293 while s.consume(','):
294 _p_ws(s)
295 k = _p_key(s)
296 _p_ws(s)
297 s.expect('=')
298 _p_ws(s)
299 items[k] = _p_value(s, object_pairs_hook=object_pairs_hook)
300 _p_ws(s)
301 s.expect('}')
302 return 'table', None, items, pos
303
304 s.fail()
305
306 def _p_stmt(s, object_pairs_hook):
307 pos = s.pos()
308 if s.consume( '['):
309 is_array = s.consume('[')
310 _p_ws(s)
311 keys = [_p_key(s)]
312 _p_ws(s)
313 while s.consume('.'):
314 _p_ws(s)
315 keys.append(_p_key(s))
316 _p_ws(s)
317 s.expect(']')
318 if is_array:
319 s.expect(']')
320 return 'table_array' if is_array else 'table', keys, pos
321
322 key = _p_key(s)
323 _p_ws(s)
324 s.expect('=')
325 _p_ws(s)
326 value = _p_value(s, object_pairs_hook=object_pairs_hook)
327 return 'kv', (key, value), pos
328
329 _stmtsep_re = re.compile(r'(?:[ \t]*(?:#[^\n]*)?\n)+[ \t]*')
330 def _p_toml(s, object_pairs_hook):
331 stmts = []
332 _p_ews(s)
333 with s:
334 stmts.append(_p_stmt(s, object_pairs_hook=object_pairs_hook))
335 while True:
336 s.commit()
337 s.expect_re(_stmtsep_re)
338 stmts.append(_p_stmt(s, object_pairs_hook=object_pairs_hook))
339 _p_ews(s)
340 s.expect_eof()
341 return stmts
0 import re, sys
1 from .core import TomlError
2 from .utils import rfc3339_re, parse_rfc3339_re
3
4 if sys.version_info[0] == 2:
5 _chr = unichr
6 else:
7 _chr = chr
8
9 def load(fin, translate=lambda t, x, v: v, object_pairs_hook=dict):
10 return loads(fin.read(), translate=translate, object_pairs_hook=object_pairs_hook, filename=getattr(fin, 'name', repr(fin)))
11
12 def loads(s, filename='<string>', translate=lambda t, x, v: v, object_pairs_hook=dict):
13 if isinstance(s, bytes):
14 s = s.decode('utf-8')
15
16 s = s.replace('\r\n', '\n')
17
18 root = object_pairs_hook()
19 tables = object_pairs_hook()
20 scope = root
21
22 src = _Source(s, filename=filename)
23 ast = _p_toml(src, object_pairs_hook=object_pairs_hook)
24
25 def error(msg):
26 raise TomlError(msg, pos[0], pos[1], filename)
27
28 def process_value(v, object_pairs_hook):
29 kind, text, value, pos = v
30 if kind == 'array':
31 if value and any(k != value[0][0] for k, t, v, p in value[1:]):
32 error('array-type-mismatch')
33 value = [process_value(item, object_pairs_hook=object_pairs_hook) for item in value]
34 elif kind == 'table':
35 value = object_pairs_hook([(k, process_value(value[k], object_pairs_hook=object_pairs_hook)) for k in value])
36 return translate(kind, text, value)
37
38 for kind, value, pos in ast:
39 if kind == 'kv':
40 k, v = value
41 if k in scope:
42 error('duplicate_keys. Key "{0}" was used more than once.'.format(k))
43 scope[k] = process_value(v, object_pairs_hook=object_pairs_hook)
44 else:
45 is_table_array = (kind == 'table_array')
46 cur = tables
47 for name in value[:-1]:
48 if isinstance(cur.get(name), list):
49 d, cur = cur[name][-1]
50 else:
51 d, cur = cur.setdefault(name, (None, object_pairs_hook()))
52
53 scope = object_pairs_hook()
54 name = value[-1]
55 if name not in cur:
56 if is_table_array:
57 cur[name] = [(scope, object_pairs_hook())]
58 else:
59 cur[name] = (scope, object_pairs_hook())
60 elif isinstance(cur[name], list):
61 if not is_table_array:
62 error('table_type_mismatch')
63 cur[name].append((scope, object_pairs_hook()))
64 else:
65 if is_table_array:
66 error('table_type_mismatch')
67 old_scope, next_table = cur[name]
68 if old_scope is not None:
69 error('duplicate_tables')
70 cur[name] = (scope, next_table)
71
72 def merge_tables(scope, tables):
73 if scope is None:
74 scope = object_pairs_hook()
75 for k in tables:
76 if k in scope:
77 error('key_table_conflict')
78 v = tables[k]
79 if isinstance(v, list):
80 scope[k] = [merge_tables(sc, tbl) for sc, tbl in v]
81 else:
82 scope[k] = merge_tables(v[0], v[1])
83 return scope
84
85 return merge_tables(root, tables)
86
87 class _Source:
88 def __init__(self, s, filename=None):
89 self.s = s
90 self._pos = (1, 1)
91 self._last = None
92 self._filename = filename
93 self.backtrack_stack = []
94
95 def last(self):
96 return self._last
97
98 def pos(self):
99 return self._pos
100
101 def fail(self):
102 return self._expect(None)
103
104 def consume_dot(self):
105 if self.s:
106 self._last = self.s[0]
107 self.s = self[1:]
108 self._advance(self._last)
109 return self._last
110 return None
111
112 def expect_dot(self):
113 return self._expect(self.consume_dot())
114
115 def consume_eof(self):
116 if not self.s:
117 self._last = ''
118 return True
119 return False
120
121 def expect_eof(self):
122 return self._expect(self.consume_eof())
123
124 def consume(self, s):
125 if self.s.startswith(s):
126 self.s = self.s[len(s):]
127 self._last = s
128 self._advance(s)
129 return True
130 return False
131
132 def expect(self, s):
133 return self._expect(self.consume(s))
134
135 def consume_re(self, re):
136 m = re.match(self.s)
137 if m:
138 self.s = self.s[len(m.group(0)):]
139 self._last = m
140 self._advance(m.group(0))
141 return m
142 return None
143
144 def expect_re(self, re):
145 return self._expect(self.consume_re(re))
146
147 def __enter__(self):
148 self.backtrack_stack.append((self.s, self._pos))
149
150 def __exit__(self, type, value, traceback):
151 if type is None:
152 self.backtrack_stack.pop()
153 else:
154 self.s, self._pos = self.backtrack_stack.pop()
155 return type == TomlError
156
157 def commit(self):
158 self.backtrack_stack[-1] = (self.s, self._pos)
159
160 def _expect(self, r):
161 if not r:
162 raise TomlError('msg', self._pos[0], self._pos[1], self._filename)
163 return r
164
165 def _advance(self, s):
166 suffix_pos = s.rfind('\n')
167 if suffix_pos == -1:
168 self._pos = (self._pos[0], self._pos[1] + len(s))
169 else:
170 self._pos = (self._pos[0] + s.count('\n'), len(s) - suffix_pos)
171
172 _ews_re = re.compile(r'(?:[ \t]|#[^\n]*\n|#[^\n]*\Z|\n)*')
173 def _p_ews(s):
174 s.expect_re(_ews_re)
175
176 _ws_re = re.compile(r'[ \t]*')
177 def _p_ws(s):
178 s.expect_re(_ws_re)
179
180 _escapes = { 'b': '\b', 'n': '\n', 'r': '\r', 't': '\t', '"': '"',
181 '\\': '\\', 'f': '\f' }
182
183 _basicstr_re = re.compile(r'[^"\\\000-\037]*')
184 _short_uni_re = re.compile(r'u([0-9a-fA-F]{4})')
185 _long_uni_re = re.compile(r'U([0-9a-fA-F]{8})')
186 _escapes_re = re.compile(r'[btnfr\"\\]')
187 _newline_esc_re = re.compile('\n[ \t\n]*')
188 def _p_basicstr_content(s, content=_basicstr_re):
189 res = []
190 while True:
191 res.append(s.expect_re(content).group(0))
192 if not s.consume('\\'):
193 break
194 if s.consume_re(_newline_esc_re):
195 pass
196 elif s.consume_re(_short_uni_re) or s.consume_re(_long_uni_re):
197 v = int(s.last().group(1), 16)
198 if 0xd800 <= v < 0xe000:
199 s.fail()
200 res.append(_chr(v))
201 else:
202 s.expect_re(_escapes_re)
203 res.append(_escapes[s.last().group(0)])
204 return ''.join(res)
205
206 _key_re = re.compile(r'[0-9a-zA-Z-_]+')
207 def _p_key(s):
208 with s:
209 s.expect('"')
210 r = _p_basicstr_content(s, _basicstr_re)
211 s.expect('"')
212 return r
213 if s.consume('\''):
214 if s.consume('\'\''):
215 s.consume('\n')
216 r = s.expect_re(_litstr_ml_re).group(0)
217 s.expect('\'\'\'')
218 else:
219 r = s.expect_re(_litstr_re).group(0)
220 s.expect('\'')
221 return r
222 return s.expect_re(_key_re).group(0)
223
224 _float_re = re.compile(r'[+-]?(?:0|[1-9](?:_?\d)*)(?:\.\d(?:_?\d)*)?(?:[eE][+-]?(?:\d(?:_?\d)*))?')
225
226 _basicstr_ml_re = re.compile(r'(?:""?(?!")|[^"\\\000-\011\013-\037])*')
227 _litstr_re = re.compile(r"[^'\000\010\012-\037]*")
228 _litstr_ml_re = re.compile(r"(?:(?:|'|'')(?:[^'\000-\010\013-\037]))*")
229 def _p_value(s, object_pairs_hook):
230 pos = s.pos()
231
232 if s.consume('true'):
233 return 'bool', s.last(), True, pos
234 if s.consume('false'):
235 return 'bool', s.last(), False, pos
236
237 if s.consume('"'):
238 if s.consume('""'):
239 s.consume('\n')
240 r = _p_basicstr_content(s, _basicstr_ml_re)
241 s.expect('"""')
242 else:
243 r = _p_basicstr_content(s, _basicstr_re)
244 s.expect('"')
245 return 'str', r, r, pos
246
247 if s.consume('\''):
248 if s.consume('\'\''):
249 s.consume('\n')
250 r = s.expect_re(_litstr_ml_re).group(0)
251 s.expect('\'\'\'')
252 else:
253 r = s.expect_re(_litstr_re).group(0)
254 s.expect('\'')
255 return 'str', r, r, pos
256
257 if s.consume_re(rfc3339_re):
258 m = s.last()
259 return 'datetime', m.group(0), parse_rfc3339_re(m), pos
260
261 if s.consume_re(_float_re):
262 m = s.last().group(0)
263 r = m.replace('_','')
264 if '.' in m or 'e' in m or 'E' in m:
265 return 'float', m, float(r), pos
266 else:
267 return 'int', m, int(r, 10), pos
268
269 if s.consume('['):
270 items = []
271 with s:
272 while True:
273 _p_ews(s)
274 items.append(_p_value(s, object_pairs_hook=object_pairs_hook))
275 s.commit()
276 _p_ews(s)
277 s.expect(',')
278 s.commit()
279 _p_ews(s)
280 s.expect(']')
281 return 'array', None, items, pos
282
283 if s.consume('{'):
284 _p_ws(s)
285 items = object_pairs_hook()
286 if not s.consume('}'):
287 k = _p_key(s)
288 _p_ws(s)
289 s.expect('=')
290 _p_ws(s)
291 items[k] = _p_value(s, object_pairs_hook=object_pairs_hook)
292 _p_ws(s)
293 while s.consume(','):
294 _p_ws(s)
295 k = _p_key(s)
296 _p_ws(s)
297 s.expect('=')
298 _p_ws(s)
299 items[k] = _p_value(s, object_pairs_hook=object_pairs_hook)
300 _p_ws(s)
301 s.expect('}')
302 return 'table', None, items, pos
303
304 s.fail()
305
306 def _p_stmt(s, object_pairs_hook):
307 pos = s.pos()
308 if s.consume( '['):
309 is_array = s.consume('[')
310 _p_ws(s)
311 keys = [_p_key(s)]
312 _p_ws(s)
313 while s.consume('.'):
314 _p_ws(s)
315 keys.append(_p_key(s))
316 _p_ws(s)
317 s.expect(']')
318 if is_array:
319 s.expect(']')
320 return 'table_array' if is_array else 'table', keys, pos
321
322 key = _p_key(s)
323 _p_ws(s)
324 s.expect('=')
325 _p_ws(s)
326 value = _p_value(s, object_pairs_hook=object_pairs_hook)
327 return 'kv', (key, value), pos
328
329 _stmtsep_re = re.compile(r'(?:[ \t]*(?:#[^\n]*)?\n)+[ \t]*')
330 def _p_toml(s, object_pairs_hook):
331 stmts = []
332 _p_ews(s)
333 with s:
334 stmts.append(_p_stmt(s, object_pairs_hook=object_pairs_hook))
335 while True:
336 s.commit()
337 s.expect_re(_stmtsep_re)
338 stmts.append(_p_stmt(s, object_pairs_hook=object_pairs_hook))
339 _p_ews(s)
340 s.expect_eof()
341 return stmts
0 import datetime
1 from .utils import format_rfc3339
2
3 try:
4 _string_types = (str, unicode)
5 _int_types = (int, long)
6 except NameError:
7 _string_types = str
8 _int_types = int
9
10 def translate_to_test(v):
11 if isinstance(v, dict):
12 return { k: translate_to_test(v) for k, v in v.items() }
13 if isinstance(v, list):
14 a = [translate_to_test(x) for x in v]
15 if v and isinstance(v[0], dict):
16 return a
17 else:
18 return {'type': 'array', 'value': a}
19 if isinstance(v, datetime.datetime):
20 return {'type': 'datetime', 'value': format_rfc3339(v)}
21 if isinstance(v, bool):
22 return {'type': 'bool', 'value': 'true' if v else 'false'}
23 if isinstance(v, _int_types):
24 return {'type': 'integer', 'value': str(v)}
25 if isinstance(v, float):
26 return {'type': 'float', 'value': '{:.17}'.format(v)}
27 if isinstance(v, _string_types):
28 return {'type': 'string', 'value': v}
29 raise RuntimeError('unexpected value: {!r}'.format(v))
0 import datetime
1 from .utils import format_rfc3339
2
3 try:
4 _string_types = (str, unicode)
5 _int_types = (int, long)
6 except NameError:
7 _string_types = str
8 _int_types = int
9
10 def translate_to_test(v):
11 if isinstance(v, dict):
12 return { k: translate_to_test(v) for k, v in v.items() }
13 if isinstance(v, list):
14 a = [translate_to_test(x) for x in v]
15 if v and isinstance(v[0], dict):
16 return a
17 else:
18 return {'type': 'array', 'value': a}
19 if isinstance(v, datetime.datetime):
20 return {'type': 'datetime', 'value': format_rfc3339(v)}
21 if isinstance(v, bool):
22 return {'type': 'bool', 'value': 'true' if v else 'false'}
23 if isinstance(v, _int_types):
24 return {'type': 'integer', 'value': str(v)}
25 if isinstance(v, float):
26 return {'type': 'float', 'value': '{:.17}'.format(v)}
27 if isinstance(v, _string_types):
28 return {'type': 'string', 'value': v}
29 raise RuntimeError('unexpected value: {!r}'.format(v))
0 import datetime
1 import re
2
3 rfc3339_re = re.compile(r'(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(?:Z|([+-]\d{2}):(\d{2}))')
4
5 def parse_rfc3339(v):
6 m = rfc3339_re.match(v)
7 if not m or m.group(0) != v:
8 return None
9 return parse_rfc3339_re(m)
10
11 def parse_rfc3339_re(m):
12 r = map(int, m.groups()[:6])
13 if m.group(7):
14 micro = float(m.group(7))
15 else:
16 micro = 0
17
18 if m.group(8):
19 g = int(m.group(8), 10) * 60 + int(m.group(9), 10)
20 tz = _TimeZone(datetime.timedelta(0, g * 60))
21 else:
22 tz = _TimeZone(datetime.timedelta(0, 0))
23
24 y, m, d, H, M, S = r
25 return datetime.datetime(y, m, d, H, M, S, int(micro * 1000000), tz)
26
27
28 def format_rfc3339(v):
29 offs = v.utcoffset()
30 offs = int(offs.total_seconds()) // 60 if offs is not None else 0
31
32 if offs == 0:
33 suffix = 'Z'
34 else:
35 if offs > 0:
36 suffix = '+'
37 else:
38 suffix = '-'
39 offs = -offs
40 suffix = '{0}{1:02}:{2:02}'.format(suffix, offs // 60, offs % 60)
41
42 if v.microsecond:
43 return v.strftime('%Y-%m-%dT%H:%M:%S.%f') + suffix
44 else:
45 return v.strftime('%Y-%m-%dT%H:%M:%S') + suffix
46
47 class _TimeZone(datetime.tzinfo):
48 def __init__(self, offset):
49 self._offset = offset
50
51 def utcoffset(self, dt):
52 return self._offset
53
54 def dst(self, dt):
55 return None
56
57 def tzname(self, dt):
58 m = self._offset.total_seconds() // 60
59 if m < 0:
60 res = '-'
61 m = -m
62 else:
63 res = '+'
64 h = m // 60
65 m = m - h * 60
66 return '{}{:.02}{:.02}'.format(res, h, m)
0 import datetime
1 import re
2
3 rfc3339_re = re.compile(r'(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(?:Z|([+-]\d{2}):(\d{2}))')
4
5 def parse_rfc3339(v):
6 m = rfc3339_re.match(v)
7 if not m or m.group(0) != v:
8 return None
9 return parse_rfc3339_re(m)
10
11 def parse_rfc3339_re(m):
12 r = map(int, m.groups()[:6])
13 if m.group(7):
14 micro = float(m.group(7))
15 else:
16 micro = 0
17
18 if m.group(8):
19 g = int(m.group(8), 10) * 60 + int(m.group(9), 10)
20 tz = _TimeZone(datetime.timedelta(0, g * 60))
21 else:
22 tz = _TimeZone(datetime.timedelta(0, 0))
23
24 y, m, d, H, M, S = r
25 return datetime.datetime(y, m, d, H, M, S, int(micro * 1000000), tz)
26
27
28 def format_rfc3339(v):
29 offs = v.utcoffset()
30 offs = int(offs.total_seconds()) // 60 if offs is not None else 0
31
32 if offs == 0:
33 suffix = 'Z'
34 else:
35 if offs > 0:
36 suffix = '+'
37 else:
38 suffix = '-'
39 offs = -offs
40 suffix = '{0}{1:02}:{2:02}'.format(suffix, offs // 60, offs % 60)
41
42 if v.microsecond:
43 return v.strftime('%Y-%m-%dT%H:%M:%S.%f') + suffix
44 else:
45 return v.strftime('%Y-%m-%dT%H:%M:%S') + suffix
46
47 class _TimeZone(datetime.tzinfo):
48 def __init__(self, offset):
49 self._offset = offset
50
51 def utcoffset(self, dt):
52 return self._offset
53
54 def dst(self, dt):
55 return None
56
57 def tzname(self, dt):
58 m = self._offset.total_seconds() // 60
59 if m < 0:
60 res = '-'
61 m = -m
62 else:
63 res = '+'
64 h = m // 60
65 m = m - h * 60
66 return '{}{:.02}{:.02}'.format(res, h, m)
0 from __future__ import unicode_literals
1 import io, datetime, math, string, sys
2
3 from .utils import format_rfc3339
4
5 try:
6 from pathlib import PurePath as _path_types
7 except ImportError:
8 _path_types = ()
9
10
11 if sys.version_info[0] == 3:
12 long = int
13 unicode = str
14
15
16 def dumps(obj, sort_keys=False):
17 fout = io.StringIO()
18 dump(obj, fout, sort_keys=sort_keys)
19 return fout.getvalue()
20
21
22 _escapes = {'\n': 'n', '\r': 'r', '\\': '\\', '\t': 't', '\b': 'b', '\f': 'f', '"': '"'}
23
24
25 def _escape_string(s):
26 res = []
27 start = 0
28
29 def flush():
30 if start != i:
31 res.append(s[start:i])
32 return i + 1
33
34 i = 0
35 while i < len(s):
36 c = s[i]
37 if c in '"\\\n\r\t\b\f':
38 start = flush()
39 res.append('\\' + _escapes[c])
40 elif ord(c) < 0x20:
41 start = flush()
42 res.append('\\u%04x' % ord(c))
43 i += 1
44
45 flush()
46 return '"' + ''.join(res) + '"'
47
48
49 _key_chars = string.digits + string.ascii_letters + '-_'
50 def _escape_id(s):
51 if any(c not in _key_chars for c in s):
52 return _escape_string(s)
53 return s
54
55
56 def _format_value(v):
57 if isinstance(v, bool):
58 return 'true' if v else 'false'
59 if isinstance(v, int) or isinstance(v, long):
60 return unicode(v)
61 if isinstance(v, float):
62 if math.isnan(v) or math.isinf(v):
63 raise ValueError("{0} is not a valid TOML value".format(v))
64 else:
65 return repr(v)
66 elif isinstance(v, unicode) or isinstance(v, bytes):
67 return _escape_string(v)
68 elif isinstance(v, datetime.datetime):
69 return format_rfc3339(v)
70 elif isinstance(v, list):
71 return '[{0}]'.format(', '.join(_format_value(obj) for obj in v))
72 elif isinstance(v, dict):
73 return '{{{0}}}'.format(', '.join('{} = {}'.format(_escape_id(k), _format_value(obj)) for k, obj in v.items()))
74 elif isinstance(v, _path_types):
75 return _escape_string(str(v))
76 else:
77 raise RuntimeError(v)
78
79
80 def dump(obj, fout, sort_keys=False):
81 tables = [((), obj, False)]
82
83 while tables:
84 name, table, is_array = tables.pop()
85 if name:
86 section_name = '.'.join(_escape_id(c) for c in name)
87 if is_array:
88 fout.write('[[{0}]]\n'.format(section_name))
89 else:
90 fout.write('[{0}]\n'.format(section_name))
91
92 table_keys = sorted(table.keys()) if sort_keys else table.keys()
93 new_tables = []
94 has_kv = False
95 for k in table_keys:
96 v = table[k]
97 if isinstance(v, dict):
98 new_tables.append((name + (k,), v, False))
99 elif isinstance(v, list) and v and all(isinstance(o, dict) for o in v):
100 new_tables.extend((name + (k,), d, True) for d in v)
101 elif v is None:
102 # based on mojombo's comment: https://github.com/toml-lang/toml/issues/146#issuecomment-25019344
103 fout.write(
104 '#{} = null # To use: uncomment and replace null with value\n'.format(_escape_id(k)))
105 has_kv = True
106 else:
107 fout.write('{0} = {1}\n'.format(_escape_id(k), _format_value(v)))
108 has_kv = True
109
110 tables.extend(reversed(new_tables))
111
112 if (name or has_kv) and tables:
113 fout.write('\n')
0 from __future__ import unicode_literals
1 import io, datetime, math, string, sys
2
3 from .utils import format_rfc3339
4
5 try:
6 from pathlib import PurePath as _path_types
7 except ImportError:
8 _path_types = ()
9
10
11 if sys.version_info[0] == 3:
12 long = int
13 unicode = str
14
15
16 def dumps(obj, sort_keys=False):
17 fout = io.StringIO()
18 dump(obj, fout, sort_keys=sort_keys)
19 return fout.getvalue()
20
21
22 _escapes = {'\n': 'n', '\r': 'r', '\\': '\\', '\t': 't', '\b': 'b', '\f': 'f', '"': '"'}
23
24
25 def _escape_string(s):
26 res = []
27 start = 0
28
29 def flush():
30 if start != i:
31 res.append(s[start:i])
32 return i + 1
33
34 i = 0
35 while i < len(s):
36 c = s[i]
37 if c in '"\\\n\r\t\b\f':
38 start = flush()
39 res.append('\\' + _escapes[c])
40 elif ord(c) < 0x20:
41 start = flush()
42 res.append('\\u%04x' % ord(c))
43 i += 1
44
45 flush()
46 return '"' + ''.join(res) + '"'
47
48
49 _key_chars = string.digits + string.ascii_letters + '-_'
50 def _escape_id(s):
51 if any(c not in _key_chars for c in s):
52 return _escape_string(s)
53 return s
54
55
56 def _format_value(v):
57 if isinstance(v, bool):
58 return 'true' if v else 'false'
59 if isinstance(v, int) or isinstance(v, long):
60 return unicode(v)
61 if isinstance(v, float):
62 if math.isnan(v) or math.isinf(v):
63 raise ValueError("{0} is not a valid TOML value".format(v))
64 else:
65 return repr(v)
66 elif isinstance(v, unicode) or isinstance(v, bytes):
67 return _escape_string(v)
68 elif isinstance(v, datetime.datetime):
69 return format_rfc3339(v)
70 elif isinstance(v, list):
71 return '[{0}]'.format(', '.join(_format_value(obj) for obj in v))
72 elif isinstance(v, dict):
73 return '{{{0}}}'.format(', '.join('{} = {}'.format(_escape_id(k), _format_value(obj)) for k, obj in v.items()))
74 elif isinstance(v, _path_types):
75 return _escape_string(str(v))
76 else:
77 raise RuntimeError(v)
78
79
80 def dump(obj, fout, sort_keys=False):
81 tables = [((), obj, False)]
82
83 while tables:
84 name, table, is_array = tables.pop()
85 if name:
86 section_name = '.'.join(_escape_id(c) for c in name)
87 if is_array:
88 fout.write('[[{0}]]\n'.format(section_name))
89 else:
90 fout.write('[{0}]\n'.format(section_name))
91
92 table_keys = sorted(table.keys()) if sort_keys else table.keys()
93 new_tables = []
94 has_kv = False
95 for k in table_keys:
96 v = table[k]
97 if isinstance(v, dict):
98 new_tables.append((name + (k,), v, False))
99 elif isinstance(v, list) and v and all(isinstance(o, dict) for o in v):
100 new_tables.extend((name + (k,), d, True) for d in v)
101 elif v is None:
102 # based on mojombo's comment: https://github.com/toml-lang/toml/issues/146#issuecomment-25019344
103 fout.write(
104 '#{} = null # To use: uncomment and replace null with value\n'.format(_escape_id(k)))
105 has_kv = True
106 else:
107 fout.write('{0} = {1}\n'.format(_escape_id(k), _format_value(v)))
108 has_kv = True
109
110 tables.extend(reversed(new_tables))
111
112 if (name or has_kv) and tables:
113 fout.write('\n')
0 Metadata-Version: 2.1
1 Name: pytoml
2 Version: 0.1.21
3 Summary: A parser for TOML-0.4.0
4 Home-page: https://github.com/avakar/pytoml
5 Author: Martin Vejnár
6 Author-email: vejnar.martin@gmail.com
7 License: MIT
8 Description: [![PyPI](https://img.shields.io/pypi/v/pytoml.svg)](https://pypi.python.org/pypi/pytoml)
9 [![Build Status](https://travis-ci.org/avakar/pytoml.svg?branch=master)](https://travis-ci.org/avakar/pytoml)
10
11 # Deprecated
12
13 The pytoml project is no longer being actively maintained. Consider using the
14 [toml](https://github.com/uiri/toml) package instead.
15
16 # pytoml
17
18 This project aims at being a specs-conforming and strict parser and writer for [TOML][1] files.
19 The library currently supports [version 0.4.0][2] of the specs and runs with Python 2.7+ and 3.5+.
20
21 Install:
22
23 pip install pytoml
24
25 The interface is the same as for the standard `json` package.
26
27 >>> import pytoml as toml
28 >>> toml.loads('a = 1')
29 {'a': 1}
30 >>> with open('file.toml', 'rb') as fin:
31 ... obj = toml.load(fin)
32 >>> obj
33 {'a': 1}
34
35 The `loads` function accepts either a bytes object
36 (that gets decoded as UTF-8 with no BOM allowed),
37 or a unicode object.
38
39 Use `dump` or `dumps` to serialize a dict into TOML.
40
41 >>> print toml.dumps(obj)
42 a = 1
43
44 ## tests
45
46 To run the tests update the `toml-test` submodule:
47
48 git submodule update --init --recursive
49
50 Then run the tests:
51
52 python test/test.py
53
54 [1]: https://github.com/toml-lang/toml
55 [2]: https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md
56
57 Platform: UNKNOWN
58 Classifier: Programming Language :: Python :: 2
59 Classifier: Programming Language :: Python :: 2.7
60 Classifier: Programming Language :: Python :: 3
61 Classifier: Programming Language :: Python :: 3.5
62 Classifier: Programming Language :: Python :: 3.6
63 Classifier: Programming Language :: Python :: 3.7
64 Classifier: License :: OSI Approved :: MIT License
65 Classifier: Topic :: Software Development :: Libraries
66 Description-Content-Type: text/markdown
0 Metadata-Version: 2.1
1 Name: pytoml
2 Version: 0.1.21
3 Summary: A parser for TOML-0.4.0
4 Home-page: https://github.com/avakar/pytoml
5 Author: Martin Vejnár
6 Author-email: vejnar.martin@gmail.com
7 License: MIT
8 Platform: UNKNOWN
9 Classifier: Programming Language :: Python :: 2
10 Classifier: Programming Language :: Python :: 2.7
11 Classifier: Programming Language :: Python :: 3
12 Classifier: Programming Language :: Python :: 3.5
13 Classifier: Programming Language :: Python :: 3.6
14 Classifier: Programming Language :: Python :: 3.7
15 Classifier: License :: OSI Approved :: MIT License
16 Classifier: Topic :: Software Development :: Libraries
17 Description-Content-Type: text/markdown
18 License-File: LICENSE
19
20 [![PyPI](https://img.shields.io/pypi/v/pytoml.svg)](https://pypi.python.org/pypi/pytoml)
21 [![Build Status](https://travis-ci.org/avakar/pytoml.svg?branch=master)](https://travis-ci.org/avakar/pytoml)
22
23 # Deprecated
24
25 The pytoml project is no longer being actively maintained. Consider using the
26 [toml](https://github.com/uiri/toml) package instead.
27
28 # pytoml
29
30 This project aims at being a specs-conforming and strict parser and writer for [TOML][1] files.
31 The library currently supports [version 0.4.0][2] of the specs and runs with Python 2.7+ and 3.5+.
32
33 Install:
34
35 pip install pytoml
36
37 The interface is the same as for the standard `json` package.
38
39 >>> import pytoml as toml
40 >>> toml.loads('a = 1')
41 {'a': 1}
42 >>> with open('file.toml', 'rb') as fin:
43 ... obj = toml.load(fin)
44 >>> obj
45 {'a': 1}
46
47 The `loads` function accepts either a bytes object
48 (that gets decoded as UTF-8 with no BOM allowed),
49 or a unicode object.
50
51 Use `dump` or `dumps` to serialize a dict into TOML.
52
53 >>> print toml.dumps(obj)
54 a = 1
55
56 ## tests
57
58 To run the tests update the `toml-test` submodule:
59
60 git submodule update --init --recursive
61
62 Then run the tests:
63
64 python test/test.py
65
66 [1]: https://github.com/toml-lang/toml
67 [2]: https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md
68
69
0 [bdist_wheel]
1 universal = 1
2
3 [metadata]
4 license_file = LICENSE
5
6 [egg_info]
7 tag_build =
8 tag_date = 0
9
0 [bdist_wheel]
1 universal = 1
2
3 [metadata]
4 license_file = LICENSE
5
6 [egg_info]
7 tag_build =
8 tag_date = 0
9
0 #!/usr/bin/env python
1 # coding: utf-8
2
3 from setuptools import setup
4
5 with open('README.md', 'r') as fin:
6 long_description = fin.read()
7
8 setup(
9 name='pytoml',
10 version='0.1.21',
11
12 description='A parser for TOML-0.4.0',
13 long_description=long_description,
14 long_description_content_type='text/markdown',
15
16 author='Martin Vejnár',
17 author_email='vejnar.martin@gmail.com',
18 url='https://github.com/avakar/pytoml',
19 license='MIT',
20 packages=['pytoml'],
21 classifiers=[
22 # Supported python versions
23 'Programming Language :: Python :: 2',
24 'Programming Language :: Python :: 2.7',
25 'Programming Language :: Python :: 3',
26 'Programming Language :: Python :: 3.5',
27 'Programming Language :: Python :: 3.6',
28 'Programming Language :: Python :: 3.7',
29
30 # License
31 'License :: OSI Approved :: MIT License',
32
33 # Topics
34 'Topic :: Software Development :: Libraries',
35 ]
36 )
0 #!/usr/bin/env python
1 # coding: utf-8
2
3 from setuptools import setup
4
5 with open('README.md', 'r') as fin:
6 long_description = fin.read()
7
8 setup(
9 name='pytoml',
10 version='0.1.21',
11
12 description='A parser for TOML-0.4.0',
13 long_description=long_description,
14 long_description_content_type='text/markdown',
15
16 author='Martin Vejnár',
17 author_email='vejnar.martin@gmail.com',
18 url='https://github.com/avakar/pytoml',
19 license='MIT',
20 packages=['pytoml'],
21 classifiers=[
22 # Supported python versions
23 'Programming Language :: Python :: 2',
24 'Programming Language :: Python :: 2.7',
25 'Programming Language :: Python :: 3',
26 'Programming Language :: Python :: 3.5',
27 'Programming Language :: Python :: 3.6',
28 'Programming Language :: Python :: 3.7',
29
30 # License
31 'License :: OSI Approved :: MIT License',
32
33 # Topics
34 'Topic :: Software Development :: Libraries',
35 ]
36 )
0 import os, json, sys, io, traceback, argparse
1 import pytoml as toml
2 from pytoml.utils import parse_rfc3339
3
4 def is_bench_equal(a, b):
5 if isinstance(a, dict):
6 if 'type' in a:
7 if b.get('type') != a['type']:
8 return False
9
10 if a['type'] == 'float':
11 return float(a['value']) == float(b['value'])
12 if a['type'] == 'datetime':
13 x = parse_rfc3339(a['value'])
14 y = parse_rfc3339(b['value'])
15 return x == y
16 if a['type'] == 'array':
17 return is_bench_equal(a['value'], b['value'])
18 return a['value'] == b['value']
19
20 return (isinstance(b, dict) and len(a) == len(b)
21 and all(k in b and is_bench_equal(a[k], b[k]) for k in a))
22
23 if isinstance(a, list):
24 return (isinstance(b, list) and len(a) == len(b)
25 and all(is_bench_equal(x, y) for x, y in zip(a, b)))
26
27 raise RuntimeError('Invalid data in the bench JSON')
28
29 def _main():
30 ap = argparse.ArgumentParser()
31 ap.add_argument('-d', '--dir', action='append')
32 ap.add_argument('testcase', nargs='*')
33 args = ap.parse_args()
34
35 if not args.dir:
36 args.dir = [os.path.join(os.path.split(__file__)[0], 'toml-test/tests')]
37
38 succeeded = []
39 failed = []
40
41 for path in args.dir:
42 if not os.path.isdir(path):
43 print('error: not a dir: {0}'.format(path))
44 return 2
45 for top, dirnames, fnames in os.walk(path):
46 for fname in fnames:
47 if not fname.endswith('.toml'):
48 continue
49
50 if args.testcase and not any(arg in fname for arg in args.testcase):
51 continue
52
53 parse_error = None
54 try:
55 with open(os.path.join(top, fname), 'rb') as fin:
56 parsed = toml.load(fin)
57 except toml.TomlError:
58 parsed = None
59 parse_error = sys.exc_info()
60 else:
61 dumped = toml.dumps(parsed, sort_keys=False)
62 dumped_sorted = toml.dumps(parsed, sort_keys=True)
63 parsed2 = toml.loads(dumped)
64 parsed2_sorted = toml.loads(dumped_sorted)
65 if parsed != parsed2 or parsed != parsed2_sorted:
66 failed.append((fname, parsed, [parsed2, parsed2_sorted], None))
67 continue
68
69 with open(os.path.join(top, fname), 'rb') as fin:
70 parsed = toml.load(fin)
71 parsed = toml.translate_to_test(parsed)
72
73 try:
74 with io.open(os.path.join(top, fname[:-5] + '.json'), 'rt', encoding='utf-8') as fin:
75 bench = json.load(fin)
76 except IOError:
77 bench = None
78
79 if (parsed is None) != (bench is None) or (parsed is not None and not is_bench_equal(parsed, bench)):
80 failed.append((fname, parsed, bench, parse_error))
81 else:
82 succeeded.append(fname)
83
84 for f, parsed, bench, e in failed:
85 try:
86 print('failed: {}\n{}\n{}'.format(f, json.dumps(parsed, indent=4), json.dumps(bench, indent=4)))
87 except TypeError:
88 print('failed: {}\n{}\n{}'.format(f, parsed, bench))
89
90 if e:
91 traceback.print_exception(*e)
92 print('succeeded: {0}'.format(len(succeeded)))
93 return 1 if failed or not succeeded else 0
94
95 if __name__ == '__main__':
96 r = _main()
97 if r:
98 sys.exit(r)
0 import os, json, sys, io, traceback, argparse
1 import pytoml as toml
2 from pytoml.utils import parse_rfc3339
3
4 def is_bench_equal(a, b):
5 if isinstance(a, dict):
6 if 'type' in a:
7 if b.get('type') != a['type']:
8 return False
9
10 if a['type'] == 'float':
11 return float(a['value']) == float(b['value'])
12 if a['type'] == 'datetime':
13 x = parse_rfc3339(a['value'])
14 y = parse_rfc3339(b['value'])
15 return x == y
16 if a['type'] == 'array':
17 return is_bench_equal(a['value'], b['value'])
18 return a['value'] == b['value']
19
20 return (isinstance(b, dict) and len(a) == len(b)
21 and all(k in b and is_bench_equal(a[k], b[k]) for k in a))
22
23 if isinstance(a, list):
24 return (isinstance(b, list) and len(a) == len(b)
25 and all(is_bench_equal(x, y) for x, y in zip(a, b)))
26
27 raise RuntimeError('Invalid data in the bench JSON')
28
29 def _main():
30 ap = argparse.ArgumentParser()
31 ap.add_argument('-d', '--dir', action='append')
32 ap.add_argument('testcase', nargs='*')
33 args = ap.parse_args()
34
35 if not args.dir:
36 args.dir = [os.path.join(os.path.split(__file__)[0], 'toml-test/tests')]
37
38 succeeded = []
39 failed = []
40
41 for path in args.dir:
42 if not os.path.isdir(path):
43 print('error: not a dir: {0}'.format(path))
44 return 2
45 for top, dirnames, fnames in os.walk(path):
46 for fname in fnames:
47 if not fname.endswith('.toml'):
48 continue
49
50 if args.testcase and not any(arg in fname for arg in args.testcase):
51 continue
52
53 parse_error = None
54 try:
55 with open(os.path.join(top, fname), 'rb') as fin:
56 parsed = toml.load(fin)
57 except toml.TomlError:
58 parsed = None
59 parse_error = sys.exc_info()
60 else:
61 dumped = toml.dumps(parsed, sort_keys=False)
62 dumped_sorted = toml.dumps(parsed, sort_keys=True)
63 parsed2 = toml.loads(dumped)
64 parsed2_sorted = toml.loads(dumped_sorted)
65 if parsed != parsed2 or parsed != parsed2_sorted:
66 failed.append((fname, parsed, [parsed2, parsed2_sorted], None))
67 continue
68
69 with open(os.path.join(top, fname), 'rb') as fin:
70 parsed = toml.load(fin)
71 parsed = toml.translate_to_test(parsed)
72
73 try:
74 with io.open(os.path.join(top, fname[:-5] + '.json'), 'rt', encoding='utf-8') as fin:
75 bench = json.load(fin)
76 except IOError:
77 bench = None
78
79 if (parsed is None) != (bench is None) or (parsed is not None and not is_bench_equal(parsed, bench)):
80 failed.append((fname, parsed, bench, parse_error))
81 else:
82 succeeded.append(fname)
83
84 for f, parsed, bench, e in failed:
85 try:
86 print('failed: {}\n{}\n{}'.format(f, json.dumps(parsed, indent=4), json.dumps(bench, indent=4)))
87 except TypeError:
88 print('failed: {}\n{}\n{}'.format(f, parsed, bench))
89
90 if e:
91 traceback.print_exception(*e)
92 print('succeeded: {0}'.format(len(succeeded)))
93 return 1 if failed or not succeeded else 0
94
95 if __name__ == '__main__':
96 r = _main()
97 if r:
98 sys.exit(r)
0 from __future__ import unicode_literals
1
2 import collections
3 import sys
4 if sys.version_info < (2, 7):
5 from StringIO import StringIO
6 else:
7 from io import StringIO
8
9 import pytest
10
11 import pytoml as toml
12
13
14 def test_name_of_fileobj_is_used_in_errors():
15 source = StringIO("[")
16 source.name = "<source>"
17 error = pytest.raises(toml.TomlError, lambda: toml.load(source))
18 assert error.value.filename == "<source>"
19
20
21 def test_when_fileobj_has_no_name_attr_then_repr_of_fileobj_is_used_in_errors():
22 source = StringIO("[")
23 error = pytest.raises(toml.TomlError, lambda: toml.load(source))
24 assert error.value.filename == repr(source)
25
26
27 def test_object_pairs_hook():
28 source = StringIO(u"""\
29 [x.a]
30 [x.b]
31 [x.c]
32 """)
33
34 d = toml.load(source, object_pairs_hook=collections.defaultdict)
35 assert isinstance(d, collections.defaultdict)
36 assert isinstance(d['x'], collections.defaultdict)
0 from __future__ import unicode_literals
1
2 import collections
3 import sys
4 if sys.version_info < (2, 7):
5 from StringIO import StringIO
6 else:
7 from io import StringIO
8
9 import pytest
10
11 import pytoml as toml
12
13
14 def test_name_of_fileobj_is_used_in_errors():
15 source = StringIO("[")
16 source.name = "<source>"
17 error = pytest.raises(toml.TomlError, lambda: toml.load(source))
18 assert error.value.filename == "<source>"
19
20
21 def test_when_fileobj_has_no_name_attr_then_repr_of_fileobj_is_used_in_errors():
22 source = StringIO("[")
23 error = pytest.raises(toml.TomlError, lambda: toml.load(source))
24 assert error.value.filename == repr(source)
25
26
27 def test_object_pairs_hook():
28 source = StringIO(u"""\
29 [x.a]
30 [x.b]
31 [x.c]
32 """)
33
34 d = toml.load(source, object_pairs_hook=collections.defaultdict)
35 assert isinstance(d, collections.defaultdict)
36 assert isinstance(d['x'], collections.defaultdict)
0 from __future__ import unicode_literals
1
2 import pytest
3
4 import pytoml as toml
5
6
7 @pytest.mark.parametrize("value", [
8 float("NaN"),
9 float("Inf"),
10 -float("Inf"),
11 ])
12 def test_attempting_to_write_non_number_floats_raises_error(value):
13 error = pytest.raises(ValueError, lambda: toml.dumps({"value": value}))
14 assert str(error.value) == "{0} is not a valid TOML value".format(value)
15
16
17 def test_pathlib_path_objects_are_written_as_strings():
18 pathlib = pytest.importorskip("pathlib")
19 path_value = toml.dumps({"value": pathlib.Path("test-path")})
20 assert path_value == 'value = "test-path"\n'
21
22
23 def test_pathlib_purepath_objects_are_written_as_strings():
24 pathlib = pytest.importorskip("pathlib")
25 path_value = toml.dumps({"value": pathlib.PurePath("test-path")})
26 assert path_value == 'value = "test-path"\n'
27
28
29 def test_pathlib_purepath_objects_contents_are_escaped():
30 pathlib = pytest.importorskip("pathlib")
31 path_value = toml.dumps({"value": pathlib.PurePath('C:\\Escape\"this string"')})
32 assert path_value == 'value = "C:\\\\Escape\\"this string\\""\n'
0 from __future__ import unicode_literals
1
2 import pytest
3
4 import pytoml as toml
5
6
7 @pytest.mark.parametrize("value", [
8 float("NaN"),
9 float("Inf"),
10 -float("Inf"),
11 ])
12 def test_attempting_to_write_non_number_floats_raises_error(value):
13 error = pytest.raises(ValueError, lambda: toml.dumps({"value": value}))
14 assert str(error.value) == "{0} is not a valid TOML value".format(value)
15
16
17 def test_pathlib_path_objects_are_written_as_strings():
18 pathlib = pytest.importorskip("pathlib")
19 path_value = toml.dumps({"value": pathlib.Path("test-path")})
20 assert path_value == 'value = "test-path"\n'
21
22
23 def test_pathlib_purepath_objects_are_written_as_strings():
24 pathlib = pytest.importorskip("pathlib")
25 path_value = toml.dumps({"value": pathlib.PurePath("test-path")})
26 assert path_value == 'value = "test-path"\n'
27
28
29 def test_pathlib_purepath_objects_contents_are_escaped():
30 pathlib = pytest.importorskip("pathlib")
31 path_value = toml.dumps({"value": pathlib.PurePath('C:\\Escape\"this string"')})
32 assert path_value == 'value = "C:\\\\Escape\\"this string\\""\n'