markdown-extractor 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.
- markdown_extractor/__init__.py +14 -0
- markdown_extractor/blocks.py +359 -0
- markdown_extractor/extractor.py +193 -0
- markdown_extractor/html_renderer.py +150 -0
- markdown_extractor/parser.py +153 -0
- markdown_extractor/py.typed +0 -0
- markdown_extractor/section.py +320 -0
- markdown_extractor/text_renderer.py +83 -0
- markdown_extractor-0.1.0.dist-info/METADATA +676 -0
- markdown_extractor-0.1.0.dist-info/RECORD +13 -0
- markdown_extractor-0.1.0.dist-info/WHEEL +5 -0
- markdown_extractor-0.1.0.dist-info/licenses/LICENSE +201 -0
- markdown_extractor-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Render the parsed block tree to plain text — Markdown stripped.
|
|
2
|
+
|
|
3
|
+
Same subset as :mod:`markdown_extractor.html_renderer`: paragraphs, ordered /
|
|
4
|
+
unordered lists with nesting, code fences (kept verbatim), blockquotes,
|
|
5
|
+
and the common inline markers (``**bold**``, ``*em*``, ``` `code` ```,
|
|
6
|
+
``[text](url)``, ````).
|
|
7
|
+
|
|
8
|
+
Output rules:
|
|
9
|
+
- Paragraphs are separated by one blank line.
|
|
10
|
+
- List items are emitted with a ``- `` (unordered) or ``1. `` (ordered)
|
|
11
|
+
marker; nested children indent by four spaces.
|
|
12
|
+
- Code blocks reproduce their original lines unchanged (no marker).
|
|
13
|
+
- Blockquotes prefix each output line with ``> ``.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
from typing import TYPE_CHECKING, Iterable, List
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from markdown_extractor.blocks import Block
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_INLINE_CODE_RE = re.compile(r"`([^`]+)`")
|
|
26
|
+
_IMG_RE = re.compile(r"!\[([^\]]*)\]\(([^)\s]+)\)")
|
|
27
|
+
_LINK_RE = re.compile(r"\[([^\]]+)\]\(([^)\s]+)\)")
|
|
28
|
+
_BOLD_RE = re.compile(r"\*\*([^*]+)\*\*")
|
|
29
|
+
_EM_STAR_RE = re.compile(r"(?<![*\w])\*([^*\n]+?)\*(?!\w)")
|
|
30
|
+
_EM_UNDER_RE = re.compile(r"(?<![\w_])_([^_\n]+?)_(?!\w)")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def strip_inline(text: str) -> str:
|
|
34
|
+
"""Remove the inline-markdown markers, leaving the visible text."""
|
|
35
|
+
if not text:
|
|
36
|
+
return ""
|
|
37
|
+
out = _INLINE_CODE_RE.sub(lambda m: m.group(1), text)
|
|
38
|
+
out = _IMG_RE.sub(lambda m: m.group(1) or m.group(2), out)
|
|
39
|
+
out = _LINK_RE.sub(lambda m: m.group(1), out)
|
|
40
|
+
out = _BOLD_RE.sub(lambda m: m.group(1), out)
|
|
41
|
+
out = _EM_STAR_RE.sub(lambda m: m.group(1), out)
|
|
42
|
+
out = _EM_UNDER_RE.sub(lambda m: m.group(1), out)
|
|
43
|
+
return out
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def render_text(blocks: Iterable[Block], indent: str = "") -> str:
|
|
47
|
+
"""Render ``blocks`` to plain text. ``indent`` is prepended to every line."""
|
|
48
|
+
parts: List[str] = []
|
|
49
|
+
for b in blocks:
|
|
50
|
+
parts.append(_render(b, indent))
|
|
51
|
+
return "\n\n".join(p for p in parts if p)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _render(block: Block, indent: str) -> str:
|
|
55
|
+
if block.kind == "paragraph":
|
|
56
|
+
return indent + strip_inline(block.text)
|
|
57
|
+
|
|
58
|
+
if block.kind == "code":
|
|
59
|
+
return "\n".join(indent + line for line in block.text.split("\n"))
|
|
60
|
+
|
|
61
|
+
if block.kind == "blockquote":
|
|
62
|
+
inner = (
|
|
63
|
+
render_text(block.children) if block.children else strip_inline(block.text)
|
|
64
|
+
)
|
|
65
|
+
out_lines: List[str] = []
|
|
66
|
+
for line in inner.split("\n"):
|
|
67
|
+
out_lines.append(indent + ("> " + line if line else ">"))
|
|
68
|
+
return "\n".join(out_lines)
|
|
69
|
+
|
|
70
|
+
if block.kind in ("list", "ordered_list"):
|
|
71
|
+
ordered = block.kind == "ordered_list"
|
|
72
|
+
item_lines: List[str] = []
|
|
73
|
+
for i, item in enumerate(block.children, start=1):
|
|
74
|
+
marker = f"{i}. " if ordered else "- "
|
|
75
|
+
head = indent + marker + strip_inline(item.text)
|
|
76
|
+
piece_lines = [head]
|
|
77
|
+
if item.children:
|
|
78
|
+
child_indent = indent + (" " * len(marker))
|
|
79
|
+
piece_lines.append(render_text(item.children, child_indent))
|
|
80
|
+
item_lines.append("\n".join(piece_lines))
|
|
81
|
+
return "\n".join(item_lines)
|
|
82
|
+
|
|
83
|
+
return indent + strip_inline(block.text)
|