from json import loads from re import sub from textwrap import dedent from . import hints def dump_scalar(entry: hints.JSONDataScalar) -> str: match entry: case None: return "null\n" case True: return "true\n" case False: return "false\n" case _: return f"{entry}\n" def dump(obj: hints.JSONDataMap) -> str: ret = "" for key, value in obj.items(): ret += key ret += ":" match value: case []: ret += " []\n" case {}: ret += " {}\n" case "": ret += ' ""\n' case list() as entries: ret += "\n" for entry in entries: match entry: case dict(): subdump = dump(entry) ret += "\n".join(" " + l for l in subdump.splitlines()) case list(): pass case _: ret += f" - {dump_scalar(entry)}" case dict() as substruct: ret += "\n" subdump = dump(substruct) ret += "\n".join(" " + l for l in subdump.splitlines()) + "\n" case str() as mlstring if key == "markdown_content": if mlstring.strip() == "" or dump_scalar(mlstring).strip() == "null": ret += ' ""\n' else: indented = "\n".join((" " + l) for l in dump_scalar(mlstring).splitlines()) ret += f" >\n{indented}\n" case entry: ret += f" {dump_scalar(entry)}" return ret def load(content: str) -> hints.JSONData: """parse subset of yaml into ``JSONData`` >>> from pprint import pprint >>> yaml = ''' ... key1: str ... key2: "str" ... key3: 1 ... key4: 2.3 ... key5: 2e12 ... key6: null ... key7: true ... key8: false ... key9: ... - 1 ... - 23.2 ... key10: ... - str ... - "str" ... key11: > ... * a ... * b: ... * c ... ''' >>> yaml_parsed = load(yaml) >>> compare = { ... 'key1': 'str', ... 'key2': 'str', ... 'key3': 1, ... 'key4': 2.3, ... 'key5': 2e12, ... 'key6': None, ... 'key7': True, ... 'key8': False, ... 'key9': [1, 23.2], ... 'key10': ['str', 'str'], ... 'key11': "* a\\n* b:\\n * c", ... } >>> {k: yaml_parsed.get(k, None) for k in compare if compare[k] != yaml_parsed[k]} {} """ ret = {} key = None value = None mlstring = False for line in sub(r"(:( >)?)[\s\n]*", r"\1\n ", dedent(content).strip()).splitlines(): if line.startswith("#") or line.strip() == "---": continue elif line.endswith(":"): if key: ret[key] = value value = None mlstring = False key = line.removesuffix(":") elif line.endswith(": >"): if key: ret[key] = value value = None mlstring = True key = line.removesuffix(": >") elif line.startswith(" -") and not mlstring: value = value or [] try: parsed = loads(line.removeprefix(" -").strip()) except: parsed = loads('"' + line.removeprefix(" -").strip() + '"') value.append(parsed) elif line.startswith(" ") and mlstring: value = value or "" value += line.removeprefix(" ") + "\n" else: try: value = loads(line.strip()) except: value = loads('"' + line.strip() + '"') if key: ret[key] = value value = None return ret if __name__ == "__main__": import doctest doctest.testmod()