|
| 1 | +# -*- coding: UTF-8 -*- |
| 2 | +"""Module for enhancing json preimport. |
| 3 | +
|
| 4 | +""" |
| 5 | +import json |
| 6 | + |
| 7 | +from ..helpers import ensure_str |
| 8 | + |
| 9 | + |
| 10 | +_CACHE = dict() |
| 11 | + |
| 12 | + |
| 13 | +def dumpc(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, |
| 14 | + separators=None, default=None, sort_keys=False, **kw): |
| 15 | + """ Serialize ``obj`` as a JSON formatted stream to ``fp`` (a ``.write()``-supporting file-like object. """ |
| 16 | + comments = _CACHE.get(id(obj), {}) |
| 17 | + indent = comments.get('indent', indent) |
| 18 | + s = json.dumps(obj, skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular, |
| 19 | + allow_nan=allow_nan, cls=cls, indent=indent, separators=separators, default=default, |
| 20 | + sort_keys=sort_keys, **kw) |
| 21 | + if indent: |
| 22 | + s, lines = "", s.split("\n") |
| 23 | + for l in lines: |
| 24 | + try: |
| 25 | + ws, c = comments.get('body', {}).get(l.strip().rstrip(",")) |
| 26 | + s += f"{l}{' '*ws}#{c}\n" |
| 27 | + except TypeError: |
| 28 | + s += f"{l}\n" |
| 29 | + s = "\n".join(f"{' '*ws}#{c}" for ws, c in comments.get('header', [])) + s |
| 30 | + fp.write(s.encode() if 'b' in fp.mode else s) |
| 31 | +json.dumpc = dumpc |
| 32 | + |
| 33 | + |
| 34 | +def loadc(fp, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, |
| 35 | + **kw): |
| 36 | + """ Deserialize ``fp`` (a ``.read()``-supporting file-like object containing a JSON document with comments) to a |
| 37 | + Python object. """ |
| 38 | + s, comments, header, indent = [], {}, True, None |
| 39 | + # collect comments from the header then from the body ; keep track of indentation |
| 40 | + for l in ensure_str(fp.read()).split("\n"): |
| 41 | + i = len(l) - len(l.lstrip()) |
| 42 | + if i > 0: |
| 43 | + indent = i if indent is None else min(indent, i) |
| 44 | + try: |
| 45 | + l, c = l.split("#", 1) |
| 46 | + ws = len(l) - len(l.rstrip()) |
| 47 | + except ValueError: |
| 48 | + c = None |
| 49 | + if header: |
| 50 | + if l.strip() == "": |
| 51 | + if c: |
| 52 | + comments.setdefault('header', []) |
| 53 | + comments['header'].append((ws, c.rstrip())) |
| 54 | + continue |
| 55 | + else: |
| 56 | + header = False |
| 57 | + s.append(l) |
| 58 | + if c: |
| 59 | + comments.setdefault('body', {}) |
| 60 | + comments['body'][l.strip().rstrip(",")] = (ws, c.rstrip()) |
| 61 | + comments['indent'] = indent |
| 62 | + # now parse the comment-free JSON |
| 63 | + obj = json.loads("\n".join(s), cls=cls, object_hook=object_hook, parse_float=parse_float, parse_int=parse_int, |
| 64 | + parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw) |
| 65 | + _CACHE[id(obj)] = comments |
| 66 | + return obj |
| 67 | +json.loadc = loadc |
| 68 | + |
0 commit comments