qparse 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.
- qmd_cli.py +262 -0
- qparse/__init__.py +16 -0
- qparse/docx_render/__init__.py +3 -0
- qparse/docx_render/docx_render.py +68 -0
- qparse/docx_render/write_buffer.py +412 -0
- qparse/markdwon_document/__init__.py +28 -0
- qparse/markdwon_document/analysis_document.py +40 -0
- qparse/markdwon_document/base_document.py +87 -0
- qparse/markdwon_document/markdwon_document.py +16 -0
- qparse/markdwon_document/nodes/__init__.py +5 -0
- qparse/markdwon_document/nodes/answer_node.py +97 -0
- qparse/markdwon_document/nodes/base_node.py +51 -0
- qparse/markdwon_document/nodes/img_node.py +31 -0
- qparse/markdwon_document/nodes/text_node.py +139 -0
- qparse/markdwon_document/question_document.py +17 -0
- qparse/markdwon_document/stem_document.py +64 -0
- qparse/markdwon_document/table_document.py +126 -0
- qparse/markdwon_loader.py +189 -0
- qparse/markdwon_render.py +123 -0
- qparse/qmd_packer.py +475 -0
- qparse/qmd_unpacker.py +169 -0
- qparse/render_option/__init__.py +40 -0
- qparse/render_option/default_render_option.py +86 -0
- qparse/render_option/default_render_template.md +21 -0
- qparse/render_option/referance.docx +0 -0
- qparse/render_option/theme.json +245 -0
- qparse/utils/__init__.py +3 -0
- qparse/utils/html_full_protector.py +154 -0
- qparse-0.1.0.dist-info/METADATA +454 -0
- qparse-0.1.0.dist-info/RECORD +34 -0
- qparse-0.1.0.dist-info/WHEEL +5 -0
- qparse-0.1.0.dist-info/entry_points.txt +2 -0
- qparse-0.1.0.dist-info/licenses/LICENSE +21 -0
- qparse-0.1.0.dist-info/top_level.txt +2 -0
qmd_cli.py
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""QParse 命令行工具:打包 / 解包 / 渲染。"""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
app = typer.Typer(
|
|
12
|
+
name="qmd",
|
|
13
|
+
help="QParse .qmd 打包、解包与渲染工具",
|
|
14
|
+
add_completion=False,
|
|
15
|
+
no_args_is_help=True,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _print_path(label: str, path: Path) -> None:
|
|
20
|
+
typer.echo(f"{label}: {path}")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
DEFAULT_RENDER_TEMPLATE = """\
|
|
24
|
+
<render src="{src}">
|
|
25
|
+
<file type="docx" dist="{dist}">
|
|
26
|
+
<doc theme="{theme}">
|
|
27
|
+
<after>
|
|
28
|
+
<pagebreak></pagebreak>
|
|
29
|
+
# 参考答案
|
|
30
|
+
|
|
31
|
+
<reference-answer></reference-answer>
|
|
32
|
+
|
|
33
|
+
</after>
|
|
34
|
+
</doc>
|
|
35
|
+
</file>
|
|
36
|
+
</render>
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def generate_render_md(
|
|
41
|
+
main_md: Path,
|
|
42
|
+
output: Optional[Path] = None,
|
|
43
|
+
dist: str = "习题版.docx",
|
|
44
|
+
theme: str = "Worksheet",
|
|
45
|
+
force: bool = False,
|
|
46
|
+
) -> Path:
|
|
47
|
+
"""根据 main.md 生成默认 render.md。"""
|
|
48
|
+
main_md = main_md.resolve()
|
|
49
|
+
if not main_md.exists():
|
|
50
|
+
raise FileNotFoundError(f"main.md 不存在: {main_md}")
|
|
51
|
+
|
|
52
|
+
# 默认写到 main.md 同级目录
|
|
53
|
+
output_path = (output or (main_md.parent / "render.md")).resolve()
|
|
54
|
+
if output_path.exists() and not force:
|
|
55
|
+
raise FileExistsError(
|
|
56
|
+
f"已存在: {output_path},如需覆盖请加 --force"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
content = DEFAULT_RENDER_TEMPLATE.format(
|
|
60
|
+
src=str(main_md),
|
|
61
|
+
dist=dist,
|
|
62
|
+
theme=theme,
|
|
63
|
+
)
|
|
64
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
65
|
+
output_path.write_text(content, encoding="utf-8")
|
|
66
|
+
return output_path
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@app.command("init")
|
|
70
|
+
def init_cmd(
|
|
71
|
+
main_md: Path = typer.Argument(
|
|
72
|
+
...,
|
|
73
|
+
exists=True,
|
|
74
|
+
dir_okay=False,
|
|
75
|
+
readable=True,
|
|
76
|
+
help="入口 main.md 路径",
|
|
77
|
+
),
|
|
78
|
+
output: Optional[Path] = typer.Option(
|
|
79
|
+
None,
|
|
80
|
+
"--output",
|
|
81
|
+
"-o",
|
|
82
|
+
help="render.md 输出路径,默认写到 main.md 同级目录",
|
|
83
|
+
),
|
|
84
|
+
dist: str = typer.Option(
|
|
85
|
+
"习题版.docx",
|
|
86
|
+
"--dist",
|
|
87
|
+
"-d",
|
|
88
|
+
help="默认 DOCX 输出文件名",
|
|
89
|
+
),
|
|
90
|
+
theme: str = typer.Option(
|
|
91
|
+
"Worksheet",
|
|
92
|
+
"--theme",
|
|
93
|
+
help="默认主题名",
|
|
94
|
+
),
|
|
95
|
+
force: bool = typer.Option(
|
|
96
|
+
False,
|
|
97
|
+
"--force",
|
|
98
|
+
"-f",
|
|
99
|
+
help="若目标已存在则覆盖",
|
|
100
|
+
),
|
|
101
|
+
) -> None:
|
|
102
|
+
"""输入 main.md,自动生成默认 render.md。"""
|
|
103
|
+
try:
|
|
104
|
+
path = generate_render_md(
|
|
105
|
+
main_md=main_md,
|
|
106
|
+
output=output,
|
|
107
|
+
dist=dist,
|
|
108
|
+
theme=theme,
|
|
109
|
+
force=force,
|
|
110
|
+
)
|
|
111
|
+
except FileExistsError as exc:
|
|
112
|
+
typer.echo(str(exc), err=True)
|
|
113
|
+
raise typer.Exit(code=1) from exc
|
|
114
|
+
|
|
115
|
+
_print_path("render", path)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@app.command("pack")
|
|
119
|
+
def pack_cmd(
|
|
120
|
+
entry: Path = typer.Argument(
|
|
121
|
+
...,
|
|
122
|
+
exists=True,
|
|
123
|
+
dir_okay=False,
|
|
124
|
+
readable=True,
|
|
125
|
+
help="入口 render.md 路径",
|
|
126
|
+
),
|
|
127
|
+
output: Optional[Path] = typer.Option(
|
|
128
|
+
None,
|
|
129
|
+
"--output",
|
|
130
|
+
"-o",
|
|
131
|
+
help="输出 .qmd 路径,默认与 entry 同名",
|
|
132
|
+
),
|
|
133
|
+
workspace: Optional[Path] = typer.Option(
|
|
134
|
+
None,
|
|
135
|
+
"--workspace",
|
|
136
|
+
"-w",
|
|
137
|
+
exists=True,
|
|
138
|
+
dir_okay=False,
|
|
139
|
+
help="code-workspace 路径,默认使用项目根目录 QParse.code-workspace",
|
|
140
|
+
),
|
|
141
|
+
) -> None:
|
|
142
|
+
"""将 render.md 及其依赖打包为 .qmd。"""
|
|
143
|
+
from qparse import QmdPacker
|
|
144
|
+
|
|
145
|
+
packer = QmdPacker(workspace=str(workspace) if workspace else None)
|
|
146
|
+
packed = packer.pack(
|
|
147
|
+
entry=str(entry),
|
|
148
|
+
output=str(output) if output else None,
|
|
149
|
+
)
|
|
150
|
+
_print_path("qmd", packed)
|
|
151
|
+
typer.echo(f"documents: {len(packer._documents)}")
|
|
152
|
+
typer.echo(f"resources: {len(packer._resources)}")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@app.command("unpack")
|
|
156
|
+
def unpack_cmd(
|
|
157
|
+
package: Path = typer.Argument(
|
|
158
|
+
...,
|
|
159
|
+
exists=True,
|
|
160
|
+
dir_okay=False,
|
|
161
|
+
readable=True,
|
|
162
|
+
help=".qmd 包路径",
|
|
163
|
+
),
|
|
164
|
+
target: Optional[Path] = typer.Option(
|
|
165
|
+
None,
|
|
166
|
+
"--target",
|
|
167
|
+
"-t",
|
|
168
|
+
help="解包目录,默认创建到包旁 .qmd_runtime/<uuid>",
|
|
169
|
+
),
|
|
170
|
+
) -> None:
|
|
171
|
+
"""解包 .qmd 到工作目录(保留,不自动删除)。"""
|
|
172
|
+
from qparse import QmdUnpacker
|
|
173
|
+
|
|
174
|
+
unpacker = QmdUnpacker(str(package))
|
|
175
|
+
out_dir = unpacker.unpack(target=str(target) if target else None)
|
|
176
|
+
_print_path("dir", out_dir)
|
|
177
|
+
_print_path("entry", unpacker.entry_path)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@app.command("render")
|
|
181
|
+
def render_cmd(
|
|
182
|
+
entry: Path = typer.Argument(
|
|
183
|
+
...,
|
|
184
|
+
exists=True,
|
|
185
|
+
dir_okay=False,
|
|
186
|
+
readable=True,
|
|
187
|
+
help="render.md 或解包后的入口文件",
|
|
188
|
+
),
|
|
189
|
+
) -> None:
|
|
190
|
+
"""根据 render.md 渲染 DOCX,并打印输出路径。"""
|
|
191
|
+
from qparse import MarkdwonRender
|
|
192
|
+
|
|
193
|
+
render = MarkdwonRender(src=str(entry))
|
|
194
|
+
outputs = render.render()
|
|
195
|
+
if not outputs:
|
|
196
|
+
typer.echo("未生成任何文件", err=True)
|
|
197
|
+
raise typer.Exit(code=1)
|
|
198
|
+
for path in outputs:
|
|
199
|
+
_print_path("docx", Path(path))
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@app.command("build")
|
|
203
|
+
def build_cmd(
|
|
204
|
+
entry: Path = typer.Argument(
|
|
205
|
+
...,
|
|
206
|
+
exists=True,
|
|
207
|
+
dir_okay=False,
|
|
208
|
+
readable=True,
|
|
209
|
+
help="入口 render.md 路径",
|
|
210
|
+
),
|
|
211
|
+
output: Optional[Path] = typer.Option(
|
|
212
|
+
None,
|
|
213
|
+
"--output",
|
|
214
|
+
"-o",
|
|
215
|
+
help="输出 .qmd 路径,默认与 entry 同名",
|
|
216
|
+
),
|
|
217
|
+
unpack_dir: Optional[Path] = typer.Option(
|
|
218
|
+
None,
|
|
219
|
+
"--unpack-dir",
|
|
220
|
+
"-u",
|
|
221
|
+
help="解包目录,默认 <output_stem>_unpacked",
|
|
222
|
+
),
|
|
223
|
+
workspace: Optional[Path] = typer.Option(
|
|
224
|
+
None,
|
|
225
|
+
"--workspace",
|
|
226
|
+
"-w",
|
|
227
|
+
exists=True,
|
|
228
|
+
dir_okay=False,
|
|
229
|
+
help="code-workspace 路径",
|
|
230
|
+
),
|
|
231
|
+
) -> None:
|
|
232
|
+
"""一键:打包 → 解包 → 渲染。中间产物均保留。"""
|
|
233
|
+
from qparse import MarkdwonRender, QmdPacker, QmdUnpacker
|
|
234
|
+
|
|
235
|
+
packer = QmdPacker(workspace=str(workspace) if workspace else None)
|
|
236
|
+
packed = packer.pack(
|
|
237
|
+
entry=str(entry),
|
|
238
|
+
output=str(output) if output else None,
|
|
239
|
+
)
|
|
240
|
+
_print_path("qmd", packed)
|
|
241
|
+
|
|
242
|
+
target = unpack_dir or packed.with_name(f"{packed.stem}_unpacked")
|
|
243
|
+
unpacker = QmdUnpacker(str(packed))
|
|
244
|
+
out_dir = unpacker.unpack(target=str(target))
|
|
245
|
+
_print_path("dir", out_dir)
|
|
246
|
+
_print_path("entry", unpacker.entry_path)
|
|
247
|
+
|
|
248
|
+
render = MarkdwonRender(src=str(unpacker.entry_path))
|
|
249
|
+
outputs = render.render()
|
|
250
|
+
if not outputs:
|
|
251
|
+
typer.echo("未生成任何文件", err=True)
|
|
252
|
+
raise typer.Exit(code=1)
|
|
253
|
+
for path in outputs:
|
|
254
|
+
_print_path("docx", Path(path))
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def main() -> None:
|
|
258
|
+
app()
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
if __name__ == "__main__":
|
|
262
|
+
main()
|
qparse/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from .markdwon_loader import MarkdownLoader
|
|
3
|
+
from .markdwon_render import MarkdwonRender
|
|
4
|
+
from .qmd_packer import QmdPacker
|
|
5
|
+
from .qmd_unpacker import QmdUnpacker
|
|
6
|
+
|
|
7
|
+
__version__ = "0.1.0"
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"MarkdownLoader",
|
|
11
|
+
"MarkdwonRender",
|
|
12
|
+
"QmdPacker",
|
|
13
|
+
"QmdUnpacker",
|
|
14
|
+
"__version__",
|
|
15
|
+
]
|
|
16
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from shutil import copy2, rmtree
|
|
5
|
+
from uuid import uuid4
|
|
6
|
+
from qparse.markdwon_document import *
|
|
7
|
+
from typing import List
|
|
8
|
+
from .write_buffer import *
|
|
9
|
+
|
|
10
|
+
class DocxRender():
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def __init__(self,MdDoc:MarkdwonDocument):
|
|
14
|
+
self._mdoc = MdDoc
|
|
15
|
+
|
|
16
|
+
self._temp_file_list = []
|
|
17
|
+
self._run_time_dir = None
|
|
18
|
+
|
|
19
|
+
def __enter__(self):
|
|
20
|
+
self._run_time_dir = self.src.parent.joinpath(
|
|
21
|
+
".qmd_runtime",
|
|
22
|
+
uuid4().hex
|
|
23
|
+
)
|
|
24
|
+
self._run_time_dir.mkdir(parents=True, exist_ok=True)
|
|
25
|
+
self.render_buffer = DocumentBuffer(self)
|
|
26
|
+
|
|
27
|
+
return self
|
|
28
|
+
|
|
29
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
30
|
+
if self._run_time_dir is not None and self._run_time_dir.exists():
|
|
31
|
+
rmtree(self._run_time_dir)
|
|
32
|
+
return False
|
|
33
|
+
@property
|
|
34
|
+
def src(self)->Path:
|
|
35
|
+
return Path( self._mdoc.attrs['src'] )
|
|
36
|
+
@property
|
|
37
|
+
def run_time_dir(self)->Path:
|
|
38
|
+
if self._run_time_dir is None:
|
|
39
|
+
self._run_time_dir = self.src.parent.joinpath(
|
|
40
|
+
".qmd_runtime",
|
|
41
|
+
uuid4().hex
|
|
42
|
+
)
|
|
43
|
+
return self._run_time_dir
|
|
44
|
+
@property
|
|
45
|
+
def dist(self):
|
|
46
|
+
return self.src.parent.joinpath("{}.docx".format(self.src.stem))
|
|
47
|
+
@property
|
|
48
|
+
def referance_path(self)->Path:
|
|
49
|
+
return Path(self._mdoc.render_option['docx']['referance'])
|
|
50
|
+
|
|
51
|
+
def create_temp_file_path(self,fext:str=".md"):
|
|
52
|
+
file_name = "{:02d}{}".format( len(self._temp_file_list)+1,fext)
|
|
53
|
+
fpath = self.run_time_dir.joinpath(file_name)
|
|
54
|
+
self._temp_file_list.append(fpath)
|
|
55
|
+
return fpath
|
|
56
|
+
def render(self,dist:str=None):
|
|
57
|
+
buffer = self.render_buffer.create(
|
|
58
|
+
type='document',keep_together=False,children=self._mdoc.dump_docx_buffer()
|
|
59
|
+
)
|
|
60
|
+
temp_file_dist = buffer.write_to_docx_file()
|
|
61
|
+
output_path = Path(dist) if dist is not None else self.dist
|
|
62
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
63
|
+
copy2(temp_file_dist, output_path)
|
|
64
|
+
|
|
65
|
+
return output_path
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|