jsonmd 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
jsonmd-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: jsonmd
3
+ Version: 0.1.0
4
+ Summary: JSON/JSON5 loader and dumper with raw multiline markdown block support
5
+ Requires-Python: >=3.9
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: json5>=0.12.0
8
+ Dynamic: description
9
+ Dynamic: description-content-type
10
+ Dynamic: requires-dist
11
+ Dynamic: requires-python
12
+ Dynamic: summary
13
+
14
+ # jsonmd
15
+
16
+ # 背景
17
+
18
+ 在人机协作时代,提示词和结构化文本配置会被开发者与 AI 代理高频共同编辑。
19
+ 可读性和低摩擦编辑能力会直接影响迭代速度与交付质量。
20
+
21
+ `jsonmd` 用来解决 JSON 不支持原生多行字符串的问题。
22
+
23
+ 通过 `<md>...</md>` 语法标记,你可以在 JSON/JSON5 中嵌入原始多行文本块
24
+ (例如 Markdown),而不需要使用 `\n` 转义符。
25
+
26
+ 这让在配置文件中存储富文本更直观、可读性更高。
27
+
28
+ 示例:
29
+
30
+ ```json5
31
+ {
32
+ content: <md># Title
33
+ - item
34
+ </md>
35
+ }
36
+ ```
37
+
38
+ 而不是:
39
+
40
+ ```json
41
+ {
42
+ "content": "# Title\n- item"
43
+ }
44
+ ```
45
+
46
+ 安装:
47
+
48
+ ```bash
49
+ pip install /path/to/lib/jsonmd
50
+ ```
51
+
52
+ 开发时可编辑安装:
53
+
54
+ ```bash
55
+ pip install -e /path/to/lib/jsonmd
56
+ ```
57
+
58
+ 使用:
59
+
60
+ ```python
61
+ from jsonmd import loads, dumps
62
+
63
+ text = """
64
+ {
65
+ content: <md># Title
66
+ - item
67
+ </md>
68
+ }
69
+ """
70
+ data = loads(text)
71
+
72
+ encoded = dumps({"content": "# Title\n- item"})
73
+ ```
74
+
75
+ # Background
76
+
77
+ In the era of human-AI collaboration, prompts and structured text configs are
78
+ edited frequently by both developers and AI agents. Readability and low-friction
79
+ editing directly affect iteration speed and quality.
80
+
81
+ `jsonmd` solves the problem that JSON does not support native multiline strings.
82
+
83
+ With `<md>...</md>` block markers, you can embed raw multiline text
84
+ (for example Markdown) in JSON/JSON5 without `\n` escaping.
85
+
86
+ This makes rich text in config files easier to read and maintain.
87
+
88
+ Example:
89
+
90
+ ```json5
91
+ {
92
+ content: <md># Title
93
+ - item
94
+ </md>
95
+ }
96
+ ```
97
+
98
+ instead of:
99
+
100
+ ```json
101
+ {
102
+ "content": "# Title\n- item"
103
+ }
104
+ ```
105
+
106
+ Install:
107
+
108
+ ```bash
109
+ pip install /path/to/lib/jsonmd
110
+ ```
111
+
112
+ Editable install for development:
113
+
114
+ ```bash
115
+ pip install -e /path/to/lib/jsonmd
116
+ ```
117
+
118
+ Usage:
119
+
120
+ ```python
121
+ from jsonmd import loads, dumps
122
+
123
+ text = """
124
+ {
125
+ content: <md># Title
126
+ - item
127
+ </md>
128
+ }
129
+ """
130
+ data = loads(text)
131
+
132
+ encoded = dumps({"content": "# Title\n- item"})
133
+ ```
jsonmd-0.1.0/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # jsonmd
2
+
3
+ # 背景
4
+
5
+ 在人机协作时代,提示词和结构化文本配置会被开发者与 AI 代理高频共同编辑。
6
+ 可读性和低摩擦编辑能力会直接影响迭代速度与交付质量。
7
+
8
+ `jsonmd` 用来解决 JSON 不支持原生多行字符串的问题。
9
+
10
+ 通过 `<md>...</md>` 语法标记,你可以在 JSON/JSON5 中嵌入原始多行文本块
11
+ (例如 Markdown),而不需要使用 `\n` 转义符。
12
+
13
+ 这让在配置文件中存储富文本更直观、可读性更高。
14
+
15
+ 示例:
16
+
17
+ ```json5
18
+ {
19
+ content: <md># Title
20
+ - item
21
+ </md>
22
+ }
23
+ ```
24
+
25
+ 而不是:
26
+
27
+ ```json
28
+ {
29
+ "content": "# Title\n- item"
30
+ }
31
+ ```
32
+
33
+ 安装:
34
+
35
+ ```bash
36
+ pip install /path/to/lib/jsonmd
37
+ ```
38
+
39
+ 开发时可编辑安装:
40
+
41
+ ```bash
42
+ pip install -e /path/to/lib/jsonmd
43
+ ```
44
+
45
+ 使用:
46
+
47
+ ```python
48
+ from jsonmd import loads, dumps
49
+
50
+ text = """
51
+ {
52
+ content: <md># Title
53
+ - item
54
+ </md>
55
+ }
56
+ """
57
+ data = loads(text)
58
+
59
+ encoded = dumps({"content": "# Title\n- item"})
60
+ ```
61
+
62
+ # Background
63
+
64
+ In the era of human-AI collaboration, prompts and structured text configs are
65
+ edited frequently by both developers and AI agents. Readability and low-friction
66
+ editing directly affect iteration speed and quality.
67
+
68
+ `jsonmd` solves the problem that JSON does not support native multiline strings.
69
+
70
+ With `<md>...</md>` block markers, you can embed raw multiline text
71
+ (for example Markdown) in JSON/JSON5 without `\n` escaping.
72
+
73
+ This makes rich text in config files easier to read and maintain.
74
+
75
+ Example:
76
+
77
+ ```json5
78
+ {
79
+ content: <md># Title
80
+ - item
81
+ </md>
82
+ }
83
+ ```
84
+
85
+ instead of:
86
+
87
+ ```json
88
+ {
89
+ "content": "# Title\n- item"
90
+ }
91
+ ```
92
+
93
+ Install:
94
+
95
+ ```bash
96
+ pip install /path/to/lib/jsonmd
97
+ ```
98
+
99
+ Editable install for development:
100
+
101
+ ```bash
102
+ pip install -e /path/to/lib/jsonmd
103
+ ```
104
+
105
+ Usage:
106
+
107
+ ```python
108
+ from jsonmd import loads, dumps
109
+
110
+ text = """
111
+ {
112
+ content: <md># Title
113
+ - item
114
+ </md>
115
+ }
116
+ """
117
+ data = loads(text)
118
+
119
+ encoded = dumps({"content": "# Title\n- item"})
120
+ ```
jsonmd-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
jsonmd-0.1.0/setup.py ADDED
@@ -0,0 +1,14 @@
1
+ from setuptools import find_packages, setup
2
+
3
+
4
+ setup(
5
+ name="jsonmd",
6
+ version="0.1.0",
7
+ description="JSON/JSON5 loader and dumper with raw multiline markdown block support",
8
+ long_description=open("README.md", encoding="utf-8").read(),
9
+ long_description_content_type="text/markdown",
10
+ python_requires=">=3.9",
11
+ package_dir={"": "src"},
12
+ packages=find_packages("src"),
13
+ install_requires=["json5>=0.12.0"],
14
+ )
@@ -0,0 +1,3 @@
1
+ from .core import dump, dumps, load, loads
2
+
3
+ __all__ = ["loads", "load", "dumps", "dump"]
@@ -0,0 +1,148 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import re
5
+ import warnings
6
+ from pathlib import Path
7
+ from typing import Any, TextIO
8
+
9
+ import json5
10
+
11
+ _MD_BLOCK_RE = re.compile(
12
+ r"(?P<prefix>:\s*)<<<(?P<tag>[A-Z][A-Z0-9_-]*)[ \t]*\n"
13
+ r"(?P<body>.*?)\n"
14
+ r"(?P=tag)(?=\s*[,}\]])",
15
+ re.S,
16
+ )
17
+ _ANGLE_BLOCK_RE = re.compile(
18
+ r"(?P<prefix>:\s*)<(?P<tag>[a-z][a-z0-9_-]*)>"
19
+ r"(?P<body>.*?)"
20
+ r"</(?P=tag)>(?P<tail>.*?)(?=\s*[,}\]])",
21
+ re.S,
22
+ )
23
+ _IDENT_RE = re.compile(r"^[A-Za-z_$][A-Za-z0-9_$]*$")
24
+
25
+
26
+ def _rewrite_markdown_blocks(text: str) -> str:
27
+ def repl_heredoc(match: re.Match[str]) -> str:
28
+ body = match.group("body")
29
+ return match.group("prefix") + json.dumps(body, ensure_ascii=False)
30
+
31
+ def repl_angle(match: re.Match[str]) -> str:
32
+ body = match.group("body")
33
+ tail = match.group("tail")
34
+ if tail.strip():
35
+ tag = match.group("tag")
36
+ warnings.warn(
37
+ f"Potential early </{tag}> close detected; "
38
+ "trailing content before separator was included as text.",
39
+ stacklevel=2,
40
+ )
41
+ body += tail
42
+ return match.group("prefix") + json.dumps(body, ensure_ascii=False)
43
+
44
+ out = _ANGLE_BLOCK_RE.sub(repl_angle, text)
45
+ out = _MD_BLOCK_RE.sub(repl_heredoc, out)
46
+ return out
47
+
48
+
49
+ def loads(text: str) -> Any:
50
+ rewritten = _rewrite_markdown_blocks(text)
51
+ return json5.loads(rewritten)
52
+
53
+
54
+ def load(path: str | Path, encoding: str = "utf-8") -> Any:
55
+ with open(path, "r", encoding=encoding) as f:
56
+ return loads(f.read())
57
+
58
+
59
+ def _choose_md_tag(text: str, base_tag: str = "MD") -> str:
60
+ lines = set(text.splitlines())
61
+ if base_tag not in lines:
62
+ return base_tag
63
+
64
+ i = 2
65
+ while True:
66
+ candidate = f"{base_tag}{i}"
67
+ if candidate not in lines:
68
+ return candidate
69
+ i += 1
70
+
71
+
72
+ def _choose_angle_tag(text: str, base_tag: str = "md") -> str:
73
+ tag = base_tag
74
+ i = 2
75
+ while f"</{tag}>" in text:
76
+ tag = f"{base_tag}{i}"
77
+ i += 1
78
+ return tag
79
+
80
+
81
+ def _dump_key(key: str) -> str:
82
+ return key if _IDENT_RE.match(key) else json5.dumps(key)
83
+
84
+
85
+ def _dump_value(
86
+ value: Any,
87
+ indent: int,
88
+ level: int,
89
+ md_tag: str,
90
+ block_style: str,
91
+ ) -> str:
92
+ if isinstance(value, dict):
93
+ if not value:
94
+ return "{}"
95
+ pad = " " * (indent * level)
96
+ child_pad = " " * (indent * (level + 1))
97
+ items = []
98
+ for k, v in value.items():
99
+ items.append(
100
+ f"{child_pad}{_dump_key(str(k))}: "
101
+ f"{_dump_value(v, indent, level + 1, md_tag, block_style)}"
102
+ )
103
+ return "{\n" + ",\n".join(items) + f"\n{pad}}}"
104
+
105
+ if isinstance(value, list):
106
+ if not value:
107
+ return "[]"
108
+ pad = " " * (indent * level)
109
+ child_pad = " " * (indent * (level + 1))
110
+ items = [
111
+ f"{child_pad}{_dump_value(v, indent, level + 1, md_tag, block_style)}"
112
+ for v in value
113
+ ]
114
+ return "[\n" + ",\n".join(items) + f"\n{pad}]"
115
+
116
+ if isinstance(value, str) and "\n" in value:
117
+ if block_style == "angle":
118
+ tag = _choose_angle_tag(value, md_tag)
119
+ return f"<{tag}>{value}</{tag}>"
120
+ tag = _choose_md_tag(value, md_tag.upper())
121
+ return f"<<<{tag}\n{value}\n{tag}"
122
+
123
+ return json5.dumps(value, ensure_ascii=False)
124
+
125
+
126
+ def dumps(
127
+ value: Any,
128
+ *,
129
+ indent: int = 2,
130
+ md_tag: str = "md",
131
+ block_style: str = "angle",
132
+ ) -> str:
133
+ if block_style not in {"angle", "heredoc"}:
134
+ raise ValueError("block_style must be 'angle' or 'heredoc'")
135
+ return _dump_value(
136
+ value, indent=indent, level=0, md_tag=md_tag, block_style=block_style
137
+ )
138
+
139
+
140
+ def dump(
141
+ value: Any,
142
+ fp: TextIO,
143
+ *,
144
+ indent: int = 2,
145
+ md_tag: str = "md",
146
+ block_style: str = "angle",
147
+ ) -> None:
148
+ fp.write(dumps(value, indent=indent, md_tag=md_tag, block_style=block_style))
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: jsonmd
3
+ Version: 0.1.0
4
+ Summary: JSON/JSON5 loader and dumper with raw multiline markdown block support
5
+ Requires-Python: >=3.9
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: json5>=0.12.0
8
+ Dynamic: description
9
+ Dynamic: description-content-type
10
+ Dynamic: requires-dist
11
+ Dynamic: requires-python
12
+ Dynamic: summary
13
+
14
+ # jsonmd
15
+
16
+ # 背景
17
+
18
+ 在人机协作时代,提示词和结构化文本配置会被开发者与 AI 代理高频共同编辑。
19
+ 可读性和低摩擦编辑能力会直接影响迭代速度与交付质量。
20
+
21
+ `jsonmd` 用来解决 JSON 不支持原生多行字符串的问题。
22
+
23
+ 通过 `<md>...</md>` 语法标记,你可以在 JSON/JSON5 中嵌入原始多行文本块
24
+ (例如 Markdown),而不需要使用 `\n` 转义符。
25
+
26
+ 这让在配置文件中存储富文本更直观、可读性更高。
27
+
28
+ 示例:
29
+
30
+ ```json5
31
+ {
32
+ content: <md># Title
33
+ - item
34
+ </md>
35
+ }
36
+ ```
37
+
38
+ 而不是:
39
+
40
+ ```json
41
+ {
42
+ "content": "# Title\n- item"
43
+ }
44
+ ```
45
+
46
+ 安装:
47
+
48
+ ```bash
49
+ pip install /path/to/lib/jsonmd
50
+ ```
51
+
52
+ 开发时可编辑安装:
53
+
54
+ ```bash
55
+ pip install -e /path/to/lib/jsonmd
56
+ ```
57
+
58
+ 使用:
59
+
60
+ ```python
61
+ from jsonmd import loads, dumps
62
+
63
+ text = """
64
+ {
65
+ content: <md># Title
66
+ - item
67
+ </md>
68
+ }
69
+ """
70
+ data = loads(text)
71
+
72
+ encoded = dumps({"content": "# Title\n- item"})
73
+ ```
74
+
75
+ # Background
76
+
77
+ In the era of human-AI collaboration, prompts and structured text configs are
78
+ edited frequently by both developers and AI agents. Readability and low-friction
79
+ editing directly affect iteration speed and quality.
80
+
81
+ `jsonmd` solves the problem that JSON does not support native multiline strings.
82
+
83
+ With `<md>...</md>` block markers, you can embed raw multiline text
84
+ (for example Markdown) in JSON/JSON5 without `\n` escaping.
85
+
86
+ This makes rich text in config files easier to read and maintain.
87
+
88
+ Example:
89
+
90
+ ```json5
91
+ {
92
+ content: <md># Title
93
+ - item
94
+ </md>
95
+ }
96
+ ```
97
+
98
+ instead of:
99
+
100
+ ```json
101
+ {
102
+ "content": "# Title\n- item"
103
+ }
104
+ ```
105
+
106
+ Install:
107
+
108
+ ```bash
109
+ pip install /path/to/lib/jsonmd
110
+ ```
111
+
112
+ Editable install for development:
113
+
114
+ ```bash
115
+ pip install -e /path/to/lib/jsonmd
116
+ ```
117
+
118
+ Usage:
119
+
120
+ ```python
121
+ from jsonmd import loads, dumps
122
+
123
+ text = """
124
+ {
125
+ content: <md># Title
126
+ - item
127
+ </md>
128
+ }
129
+ """
130
+ data = loads(text)
131
+
132
+ encoded = dumps({"content": "# Title\n- item"})
133
+ ```
@@ -0,0 +1,9 @@
1
+ README.md
2
+ setup.py
3
+ src/jsonmd/__init__.py
4
+ src/jsonmd/core.py
5
+ src/jsonmd.egg-info/PKG-INFO
6
+ src/jsonmd.egg-info/SOURCES.txt
7
+ src/jsonmd.egg-info/dependency_links.txt
8
+ src/jsonmd.egg-info/requires.txt
9
+ src/jsonmd.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ json5>=0.12.0
@@ -0,0 +1 @@
1
+ jsonmd