mistune-json 0.1.0__py3-none-any.whl
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.
mistune_json/__init__.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from typing import Any, Dict, Iterable, List, Optional
|
|
2
|
+
|
|
3
|
+
import mistune
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class JsonRenderer(mistune.HTMLRenderer):
|
|
7
|
+
"""
|
|
8
|
+
A renderer for Mistune that outputs Markdown as JSON structures
|
|
9
|
+
instead of HTML.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, output: Optional[Dict[str, Any]] = None) -> None:
|
|
13
|
+
"""
|
|
14
|
+
Initialize the JSON renderer.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
output: Optional dictionary to update with rendered content
|
|
18
|
+
"""
|
|
19
|
+
# No need for escape HTML or skip style in JSON renderer
|
|
20
|
+
super().__init__(escape=False, allow_harmful_protocols=False)
|
|
21
|
+
self._output: Dict[str, Any] = output if output is not None else {}
|
|
22
|
+
|
|
23
|
+
def render_tokens(self, tokens: Iterable[Dict[str, Any]], state: Any) -> List[Dict[str, Any]]:
|
|
24
|
+
"""
|
|
25
|
+
Render a list of tokens into JSON structures.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
tokens: Iterable of tokens to render
|
|
29
|
+
state: State object from Mistune parser
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
List of rendered JSON structures
|
|
33
|
+
"""
|
|
34
|
+
return list(self.iter_tokens(tokens, state))
|
|
35
|
+
|
|
36
|
+
def iter_tokens(self, tokens: Iterable[Dict[str, Any]], state: Any) -> Iterable[Dict[str, Any]]:
|
|
37
|
+
"""
|
|
38
|
+
Iterate through tokens and yield rendered JSON structures.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
tokens: Iterable of tokens to render
|
|
42
|
+
state: State object from Mistune parser
|
|
43
|
+
|
|
44
|
+
Yields:
|
|
45
|
+
Rendered JSON structures
|
|
46
|
+
"""
|
|
47
|
+
for tok in tokens:
|
|
48
|
+
yield self.render_token(tok, state)
|
|
49
|
+
|
|
50
|
+
def __call__(self, tokens: Iterable[Dict[str, Any]], state: Any) -> Dict[str, Any]:
|
|
51
|
+
"""
|
|
52
|
+
Main entry point for rendering.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
tokens: Iterable of tokens to render
|
|
56
|
+
state: State object from Mistune parser
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Dictionary with rendered content
|
|
60
|
+
"""
|
|
61
|
+
content = self.render_tokens(tokens, state)
|
|
62
|
+
# Filter out empty items
|
|
63
|
+
filtered_content = [item for item in content if item]
|
|
64
|
+
self._output.update({"content": filtered_content})
|
|
65
|
+
return self._output
|
|
66
|
+
|
|
67
|
+
# Block level tokens
|
|
68
|
+
|
|
69
|
+
def blank_line(self) -> Dict[str, Any]:
|
|
70
|
+
"""Render a blank line."""
|
|
71
|
+
return {}
|
|
72
|
+
|
|
73
|
+
def paragraph(self, text: str) -> Dict[str, Any]:
|
|
74
|
+
"""Render a paragraph."""
|
|
75
|
+
return {"type": "p", "content": text}
|
|
76
|
+
|
|
77
|
+
def heading(self, text: str, level: int) -> Dict[str, Any]:
|
|
78
|
+
"""Render a heading."""
|
|
79
|
+
return {"type": "h", "content": text, "level": level}
|
|
80
|
+
|
|
81
|
+
def block_code(self, code: str, info: Optional[str] = None) -> Dict[str, Any]:
|
|
82
|
+
"""Render a code block."""
|
|
83
|
+
result = {"type": "code", "content": code.strip()}
|
|
84
|
+
if info:
|
|
85
|
+
result["lang"] = info.strip()
|
|
86
|
+
return result
|
|
87
|
+
|
|
88
|
+
def block_quote(self, text: str) -> Dict[str, Any]:
|
|
89
|
+
"""Render a block quote."""
|
|
90
|
+
return {"type": "blockquote", "content": text}
|
|
91
|
+
|
|
92
|
+
def list(self, text: str, ordered: bool, **attrs: int) -> Dict[str, Any]:
|
|
93
|
+
"""Render an ordered or unordered list."""
|
|
94
|
+
if ordered:
|
|
95
|
+
result = {"type": "ol", "content": text}
|
|
96
|
+
start = attrs.get("start")
|
|
97
|
+
if start is not None:
|
|
98
|
+
result["start"] = start
|
|
99
|
+
return result
|
|
100
|
+
else:
|
|
101
|
+
return {"type": "ul", "content": text}
|
|
102
|
+
|
|
103
|
+
def list_item(self, text: str) -> Dict[str, Any]:
|
|
104
|
+
"""Render a list item."""
|
|
105
|
+
return {"content": text}
|
|
106
|
+
|
|
107
|
+
def thematic_break(self) -> Dict[str, Any]:
|
|
108
|
+
"""Render a thematic break (horizontal rule)."""
|
|
109
|
+
return {"type": "hr"}
|
|
110
|
+
|
|
111
|
+
# Span level tokens
|
|
112
|
+
|
|
113
|
+
def strong(self, text: str) -> Dict[str, Any]:
|
|
114
|
+
"""Render strong emphasis."""
|
|
115
|
+
return {"type": "strong", "content": text}
|
|
116
|
+
|
|
117
|
+
def emphasis(self, text: str) -> Dict[str, Any]:
|
|
118
|
+
"""Render emphasis."""
|
|
119
|
+
return {"type": "em", "content": text}
|
|
120
|
+
|
|
121
|
+
def codespan(self, text: str) -> Dict[str, Any]:
|
|
122
|
+
"""Render inline code."""
|
|
123
|
+
return {"type": "codespan", "content": text}
|
|
124
|
+
|
|
125
|
+
def link(self, text: str, url: str, title: Optional[str] = None) -> Dict[str, Any]:
|
|
126
|
+
"""Render a link."""
|
|
127
|
+
link = {"type": "a", "href": self.safe_url(url), "content": text}
|
|
128
|
+
if title:
|
|
129
|
+
link["title"] = title
|
|
130
|
+
return link
|
|
131
|
+
|
|
132
|
+
def image(self, text: str, url: str, title: Optional[str] = None) -> Dict[str, Any]:
|
|
133
|
+
"""Render an image."""
|
|
134
|
+
return {
|
|
135
|
+
"type": "img",
|
|
136
|
+
"src": self.safe_url(url),
|
|
137
|
+
"alt": text,
|
|
138
|
+
**({"title": title} if title else {}),
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
def linebreak(self) -> Dict[str, Any]:
|
|
142
|
+
"""Render a line break."""
|
|
143
|
+
return {"type": "br"}
|
|
144
|
+
|
|
145
|
+
def text(self, text: str) -> Dict[str, Any]:
|
|
146
|
+
"""Render plain text."""
|
|
147
|
+
return {"type": "text", "content": text}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: mistune-json
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary:
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: fernandonino
|
|
7
|
+
Author-email: nino.fernando@gmail.com
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Requires-Dist: mistune (>=3.1.3,<4.0.0)
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# Mistune JSON
|
|
17
|
+
A JSON renderer for the [Mistune](https://github.com/lepture/mistune) Markdown parser.
|
|
18
|
+
|
|
19
|
+
## Supported HTML elements
|
|
20
|
+
So far, the HTML elements supported by this renderer are limited to:
|
|
21
|
+
|
|
22
|
+
* Paragraph, `<p>`
|
|
23
|
+
* Image, `<img>`
|
|
24
|
+
* Ordered lists, `<ol>`
|
|
25
|
+
* Unordered lists, `<ul>`
|
|
26
|
+
* All heading elements, `<h1>` through `<h6>`
|
|
27
|
+
* Links, `<a>`
|
|
28
|
+
* Emphasis, `<em>`
|
|
29
|
+
* Strong, `<strong>`
|
|
30
|
+
* Blockquote, `<blockquote>`
|
|
31
|
+
* Line breake, `<br>`
|
|
32
|
+
* Thematic breake, `<hr>`
|
|
33
|
+
* Both inline and block code
|
|
34
|
+
|
|
35
|
+
## How to use the JSON renderer
|
|
36
|
+
### Installation
|
|
37
|
+
The package is not yet published in PyPI, but once it is, it will be installed with:
|
|
38
|
+
```shell
|
|
39
|
+
pip install mistune-json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Usage
|
|
43
|
+
```python
|
|
44
|
+
import mistune
|
|
45
|
+
from mistune_json import JsonRenderer
|
|
46
|
+
|
|
47
|
+
# Create a renderer instance
|
|
48
|
+
renderer = JsonRenderer()
|
|
49
|
+
|
|
50
|
+
# Create a Markdown parser with the JSON renderer
|
|
51
|
+
markdown = mistune.create_markdown(renderer=renderer)
|
|
52
|
+
|
|
53
|
+
# Parse Markdown text
|
|
54
|
+
result = markdown("# Hello, world!")
|
|
55
|
+
|
|
56
|
+
print(result)
|
|
57
|
+
# {'content': [{'type': 'h', 'content': ['type': 'text', 'content': 'Hello, world!'], 'level': 1}]}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Improvements and bugs
|
|
61
|
+
|
|
62
|
+
### Tables
|
|
63
|
+
There is currently no support for rendering tables (headers and rows). It might be added in the future, if there is enough need for it. You can rise an issue [here](https://github.com/fernandonino/mistune-json/issues) and label it as _enhancement_.
|
|
64
|
+
|
|
65
|
+
### Integration testing
|
|
66
|
+
Tests for a full Markdown document will be added to assure that the JSON render works as expected.
|
|
67
|
+
|
|
68
|
+
### Bugs
|
|
69
|
+
If you find a problem with the JSON output structure, feel free to [report a bug](https://github.com/fernandonino/mistune-json/issues).
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
mistune_json/__init__.py,sha256=YhcKlkNUFuuq9Xf_-zGDK3XJ26RxGnVHXD5H1ZR61AI,204
|
|
2
|
+
mistune_json/json_renderer.py,sha256=xusj9_pqJMC_WymRSEQWdLajcgNjNqlcLyrHGi73HPs,4792
|
|
3
|
+
mistune_json-0.1.0.dist-info/METADATA,sha256=vChrWJhlv9kTZ2Pty8PwTVo1hdfJOfUyhHwtqCSeznA,2081
|
|
4
|
+
mistune_json-0.1.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
5
|
+
mistune_json-0.1.0.dist-info/RECORD,,
|