marktip 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.
- marktip-0.1.0/.github/workflows/build-distributions.yml +65 -0
- marktip-0.1.0/.gitignore +10 -0
- marktip-0.1.0/CMakeLists.txt +40 -0
- marktip-0.1.0/LICENSE +21 -0
- marktip-0.1.0/PKG-INFO +73 -0
- marktip-0.1.0/README.md +45 -0
- marktip-0.1.0/pyproject.toml +53 -0
- marktip-0.1.0/python/marktip/__init__.py +27 -0
- marktip-0.1.0/scripts/benchmark.py +131 -0
- marktip-0.1.0/src/ast.cpp +343 -0
- marktip-0.1.0/src/ast.h +76 -0
- marktip-0.1.0/src/marktip.cpp +26 -0
- marktip-0.1.0/src/parser.cpp +748 -0
- marktip-0.1.0/src/parser.h +11 -0
- marktip-0.1.0/src/serializer.cpp +622 -0
- marktip-0.1.0/src/serializer.h +11 -0
- marktip-0.1.0/tests/test_document.py +64 -0
- marktip-0.1.0/tests/test_parse.py +85 -0
- marktip-0.1.0/tests/test_roundtrip.py +58 -0
- marktip-0.1.0/tests/test_serialize.py +141 -0
- marktip-0.1.0/third_party/md4c/LICENSE.md +22 -0
- marktip-0.1.0/third_party/md4c/src/entity.c +2185 -0
- marktip-0.1.0/third_party/md4c/src/entity.h +43 -0
- marktip-0.1.0/third_party/md4c/src/md4c.c +6462 -0
- marktip-0.1.0/third_party/md4c/src/md4c.h +407 -0
- marktip-0.1.0/third_party/md4c/upstream_metadata/README.md +12 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: Build distributions
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
tags: ["v*"]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
sdist:
|
|
15
|
+
name: Build source distribution
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v6
|
|
20
|
+
with:
|
|
21
|
+
persist-credentials: false
|
|
22
|
+
|
|
23
|
+
- uses: actions/setup-python@v6
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.x"
|
|
26
|
+
|
|
27
|
+
- name: Install build tools
|
|
28
|
+
run: python -m pip install build twine
|
|
29
|
+
|
|
30
|
+
- name: Build sdist
|
|
31
|
+
run: python -m build --sdist --outdir dist
|
|
32
|
+
|
|
33
|
+
- name: Check sdist
|
|
34
|
+
run: python -m twine check dist/*
|
|
35
|
+
|
|
36
|
+
- uses: actions/upload-artifact@v6
|
|
37
|
+
with:
|
|
38
|
+
name: marktip-sdist
|
|
39
|
+
path: dist/*.tar.gz
|
|
40
|
+
|
|
41
|
+
wheels:
|
|
42
|
+
name: Build wheels on ${{ matrix.os }}
|
|
43
|
+
runs-on: ${{ matrix.os }}
|
|
44
|
+
strategy:
|
|
45
|
+
fail-fast: false
|
|
46
|
+
matrix:
|
|
47
|
+
os: [ubuntu-latest, windows-latest, macos-15-intel, macos-latest]
|
|
48
|
+
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v6
|
|
51
|
+
with:
|
|
52
|
+
persist-credentials: false
|
|
53
|
+
|
|
54
|
+
- uses: actions/setup-python@v6
|
|
55
|
+
|
|
56
|
+
- name: Install cibuildwheel
|
|
57
|
+
run: python -m pip install cibuildwheel==4.1.0
|
|
58
|
+
|
|
59
|
+
- name: Build wheels
|
|
60
|
+
run: python -m cibuildwheel --output-dir wheelhouse
|
|
61
|
+
|
|
62
|
+
- uses: actions/upload-artifact@v6
|
|
63
|
+
with:
|
|
64
|
+
name: marktip-wheels-${{ matrix.os }}
|
|
65
|
+
path: wheelhouse/*.whl
|
marktip-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.15)
|
|
2
|
+
|
|
3
|
+
project(marktip LANGUAGES C CXX)
|
|
4
|
+
|
|
5
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
6
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
7
|
+
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
8
|
+
|
|
9
|
+
if(NOT DEFINED SKBUILD_PROJECT_VERSION)
|
|
10
|
+
set(SKBUILD_PROJECT_VERSION "0.1.0")
|
|
11
|
+
endif()
|
|
12
|
+
|
|
13
|
+
set(PYBIND11_FINDPYTHON ON)
|
|
14
|
+
find_package(pybind11 CONFIG REQUIRED)
|
|
15
|
+
|
|
16
|
+
add_library(md4c STATIC
|
|
17
|
+
third_party/md4c/src/md4c.c
|
|
18
|
+
third_party/md4c/src/entity.c
|
|
19
|
+
)
|
|
20
|
+
target_include_directories(md4c PUBLIC third_party/md4c/src)
|
|
21
|
+
target_compile_definitions(md4c PRIVATE MD4C_USE_UTF8)
|
|
22
|
+
set_target_properties(md4c PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
|
23
|
+
|
|
24
|
+
pybind11_add_module(_core
|
|
25
|
+
src/ast.cpp
|
|
26
|
+
src/marktip.cpp
|
|
27
|
+
src/parser.cpp
|
|
28
|
+
src/serializer.cpp
|
|
29
|
+
)
|
|
30
|
+
target_link_libraries(_core PRIVATE md4c)
|
|
31
|
+
target_include_directories(_core PRIVATE third_party/md4c/src)
|
|
32
|
+
target_compile_definitions(_core PRIVATE MARKTIP_VERSION="${SKBUILD_PROJECT_VERSION}")
|
|
33
|
+
|
|
34
|
+
if(MSVC)
|
|
35
|
+
target_compile_options(_core PRIVATE /O2)
|
|
36
|
+
else()
|
|
37
|
+
target_compile_options(_core PRIVATE -O3 -Wall -Wextra -Wpedantic)
|
|
38
|
+
endif()
|
|
39
|
+
|
|
40
|
+
install(TARGETS _core LIBRARY DESTINATION marktip)
|
marktip-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Minjae Kyung
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
marktip-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: marktip
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fast C++/MD4C Markdown parser and canonical Markdown serializer for Tiptap-style JSON
|
|
5
|
+
Keywords: markdown,tiptap,md4c,parser
|
|
6
|
+
Author: Saneaven (Minjae Kyung)
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
License-File: third_party/md4c/LICENSE.md
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: MacOS
|
|
13
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: C++
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Text Processing :: Markup :: Markdown
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Provides-Extra: test
|
|
26
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# marktip
|
|
30
|
+
|
|
31
|
+
Fast C++/MD4C Markdown conversion for Tiptap-style JSON.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python -m pip install marktip
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Release wheels are built for common CPython versions on Linux, macOS, and
|
|
40
|
+
Windows. If a wheel is not available for a platform, pip can build from the
|
|
41
|
+
source distribution with a C++17 compiler and standard Python build tooling.
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import marktip as tm
|
|
47
|
+
|
|
48
|
+
doc = tm.from_markdown("# Hello")
|
|
49
|
+
ast = doc.to_dict()
|
|
50
|
+
markdown = doc.to_markdown()
|
|
51
|
+
|
|
52
|
+
doc = tm.from_dict(ast)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The first version targets GFM core syntax and canonical Markdown output rather
|
|
56
|
+
than byte-identical source preservation.
|
|
57
|
+
|
|
58
|
+
## Development
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
python -m pip install .[test]
|
|
62
|
+
python -m pytest
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
For a direct local CMake build:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
|
69
|
+
-Dpybind11_DIR="$(python -m pybind11 --cmakedir)"
|
|
70
|
+
cmake --build build
|
|
71
|
+
PYTHONPATH=python python -m pytest
|
|
72
|
+
PYTHONPATH=python python scripts/benchmark.py
|
|
73
|
+
```
|
marktip-0.1.0/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# marktip
|
|
2
|
+
|
|
3
|
+
Fast C++/MD4C Markdown conversion for Tiptap-style JSON.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
python -m pip install marktip
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Release wheels are built for common CPython versions on Linux, macOS, and
|
|
12
|
+
Windows. If a wheel is not available for a platform, pip can build from the
|
|
13
|
+
source distribution with a C++17 compiler and standard Python build tooling.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import marktip as tm
|
|
19
|
+
|
|
20
|
+
doc = tm.from_markdown("# Hello")
|
|
21
|
+
ast = doc.to_dict()
|
|
22
|
+
markdown = doc.to_markdown()
|
|
23
|
+
|
|
24
|
+
doc = tm.from_dict(ast)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The first version targets GFM core syntax and canonical Markdown output rather
|
|
28
|
+
than byte-identical source preservation.
|
|
29
|
+
|
|
30
|
+
## Development
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
python -m pip install .[test]
|
|
34
|
+
python -m pytest
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For a direct local CMake build:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
|
41
|
+
-Dpybind11_DIR="$(python -m pybind11 --cmakedir)"
|
|
42
|
+
cmake --build build
|
|
43
|
+
PYTHONPATH=python python -m pytest
|
|
44
|
+
PYTHONPATH=python python scripts/benchmark.py
|
|
45
|
+
```
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["scikit-build-core>=0.10", "pybind11>=2.12"]
|
|
3
|
+
build-backend = "scikit_build_core.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "marktip"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Fast C++/MD4C Markdown parser and canonical Markdown serializer for Tiptap-style JSON"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE", "third_party/md4c/LICENSE.md"]
|
|
13
|
+
authors = [{ name = "Saneaven (Minjae Kyung)" }]
|
|
14
|
+
keywords = ["markdown", "tiptap", "md4c", "parser"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Operating System :: MacOS",
|
|
19
|
+
"Operating System :: Microsoft :: Windows",
|
|
20
|
+
"Operating System :: POSIX :: Linux",
|
|
21
|
+
"Programming Language :: C++",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.9",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Programming Language :: Python :: 3.14",
|
|
29
|
+
"Topic :: Text Processing :: Markup :: Markdown",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
test = ["pytest>=8"]
|
|
34
|
+
|
|
35
|
+
[tool.scikit-build]
|
|
36
|
+
cmake.build-type = "Release"
|
|
37
|
+
wheel.packages = ["python/marktip"]
|
|
38
|
+
sdist.include = [
|
|
39
|
+
"third_party/md4c/src/md4c.c",
|
|
40
|
+
"third_party/md4c/src/md4c.h",
|
|
41
|
+
"third_party/md4c/src/entity.c",
|
|
42
|
+
"third_party/md4c/src/entity.h",
|
|
43
|
+
"third_party/md4c/LICENSE.md",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
testpaths = ["tests"]
|
|
48
|
+
|
|
49
|
+
[tool.cibuildwheel]
|
|
50
|
+
build = "cp39-* cp310-* cp311-* cp312-* cp313-* cp314-*"
|
|
51
|
+
skip = ["*-win32", "*-manylinux_i686", "*-musllinux*"]
|
|
52
|
+
test-extras = ["test"]
|
|
53
|
+
test-command = "pytest {project}/tests"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Fast Markdown conversion for Tiptap-style JSON."""
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
from ._core import Document, __version__, from_dict, from_markdown
|
|
5
|
+
except ImportError: # pragma: no cover - development-only CMake build fallback
|
|
6
|
+
import importlib.util
|
|
7
|
+
import pathlib
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
_root = pathlib.Path(__file__).resolve().parents[2]
|
|
11
|
+
_candidates = sorted(_root.glob("build/**/_core*.so")) + sorted(_root.glob("build/**/_core*.pyd"))
|
|
12
|
+
if not _candidates:
|
|
13
|
+
raise
|
|
14
|
+
|
|
15
|
+
_spec = importlib.util.spec_from_file_location("marktip._core", _candidates[0])
|
|
16
|
+
if _spec is None or _spec.loader is None:
|
|
17
|
+
raise
|
|
18
|
+
_module = importlib.util.module_from_spec(_spec)
|
|
19
|
+
sys.modules[_spec.name] = _module
|
|
20
|
+
_spec.loader.exec_module(_module)
|
|
21
|
+
|
|
22
|
+
__version__ = _module.__version__
|
|
23
|
+
Document = _module.Document
|
|
24
|
+
from_dict = _module.from_dict
|
|
25
|
+
from_markdown = _module.from_markdown
|
|
26
|
+
|
|
27
|
+
__all__ = ["__version__", "Document", "from_markdown", "from_dict"]
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Local performance smoke test with a large mixed Markdown document.
|
|
2
|
+
|
|
3
|
+
Run after building/installing the package:
|
|
4
|
+
|
|
5
|
+
PYTHONPATH=python python scripts/benchmark.py
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import concurrent.futures
|
|
12
|
+
import time
|
|
13
|
+
import tracemalloc
|
|
14
|
+
|
|
15
|
+
import marktip as tm
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
LOREM = (
|
|
19
|
+
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
|
|
20
|
+
"Praesent vitae augue sed neque efficitur vulputate. "
|
|
21
|
+
"Integer non sem at orci pretium aliquet. "
|
|
22
|
+
"Suspendisse potenti, sed fermentum risus sagittis in. "
|
|
23
|
+
"Curabitur blandit, nibh in dignissim gravida, sapien risus feugiat arcu, "
|
|
24
|
+
"vel pretium erat sem vitae erat. "
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def lorem_words(repetitions: int) -> str:
|
|
29
|
+
return LOREM * repetitions
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def make_fixture(sections: int = 1_200, lorem_repetitions: int = 4) -> str:
|
|
33
|
+
chunks: list[str] = ["# Synthetic Lorem Markdown Corpus\n\n"]
|
|
34
|
+
long_lorem = lorem_words(lorem_repetitions)
|
|
35
|
+
|
|
36
|
+
for index in range(sections):
|
|
37
|
+
checked = "x" if index % 2 == 0 else " "
|
|
38
|
+
chunks.append(
|
|
39
|
+
f"## Section {index}: Lorem Throughput\n\n"
|
|
40
|
+
f"{long_lorem}"
|
|
41
|
+
f"This paragraph mixes **bold emphasis {index}**, *italic phrasing*, "
|
|
42
|
+
f"~~deleted context~~, `inline_code_{index}`, "
|
|
43
|
+
f"[reference link](https://example.com/docs/{index}), and <span data-i=\"{index}\">inline HTML</span>. \n"
|
|
44
|
+
f"It also keeps a hard break before continuing with more prose. {long_lorem}\n\n"
|
|
45
|
+
f"> {long_lorem}"
|
|
46
|
+
f"> Nested quotation with **strong text**, [a citation](https://example.com/cite/{index}), "
|
|
47
|
+
f"> and enough lorem ipsum to look like a real imported document.\n\n"
|
|
48
|
+
f"- [{checked}] Review generated section {index}\n"
|
|
49
|
+
f"- [ ] Normalize serializer output for section {index}\n"
|
|
50
|
+
f"- [ ] Compare parse and serialization latency\n\n"
|
|
51
|
+
f"1. Ordered item alpha for section {index}\n"
|
|
52
|
+
f"2. Ordered item beta with `code` and **marks**\n"
|
|
53
|
+
f" - Nested bullet carries {long_lorem[:180]}\n\n"
|
|
54
|
+
"| Metric | Value | Notes |\n"
|
|
55
|
+
"| :--- | ---: | :--- |\n"
|
|
56
|
+
f"| section | {index} | lorem table cell with **markdown** and pipes escaped later |\n"
|
|
57
|
+
f"| bytes | {len(long_lorem)} | [source](https://example.com/bench/{index}) |\n\n"
|
|
58
|
+
"```cpp\n"
|
|
59
|
+
f"// synthetic code block for section {index}\n"
|
|
60
|
+
"int accumulate_markdown_nodes(int base) {\n"
|
|
61
|
+
" int total = base;\n"
|
|
62
|
+
" for (int i = 0; i < 32; ++i) {\n"
|
|
63
|
+
" total += i * 3;\n"
|
|
64
|
+
" }\n"
|
|
65
|
+
" return total;\n"
|
|
66
|
+
"}\n"
|
|
67
|
+
"```\n\n"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if index % 10 == 0:
|
|
71
|
+
chunks.append(f"<section data-benchmark=\"{index}\">raw html block</section>\n\n")
|
|
72
|
+
|
|
73
|
+
return "".join(chunks)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def count_nodes(node: dict) -> int:
|
|
77
|
+
return 1 + sum(count_nodes(child) for child in node.get("content", []))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def mib(value: int) -> float:
|
|
81
|
+
return value / (1024 * 1024)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def main() -> None:
|
|
85
|
+
parser = argparse.ArgumentParser()
|
|
86
|
+
parser.add_argument("--sections", type=int, default=1_200)
|
|
87
|
+
parser.add_argument("--lorem-repetitions", type=int, default=4)
|
|
88
|
+
parser.add_argument("--threads", type=int, default=4)
|
|
89
|
+
args = parser.parse_args()
|
|
90
|
+
|
|
91
|
+
markdown = make_fixture(args.sections, args.lorem_repetitions)
|
|
92
|
+
input_bytes = len(markdown.encode("utf-8"))
|
|
93
|
+
size_mb = mib(input_bytes)
|
|
94
|
+
|
|
95
|
+
start = time.perf_counter()
|
|
96
|
+
doc = tm.from_markdown(markdown)
|
|
97
|
+
parse_seconds = time.perf_counter() - start
|
|
98
|
+
|
|
99
|
+
start = time.perf_counter()
|
|
100
|
+
ast = doc.to_dict()
|
|
101
|
+
to_dict_seconds = time.perf_counter() - start
|
|
102
|
+
node_count = count_nodes(ast)
|
|
103
|
+
|
|
104
|
+
tracemalloc.start()
|
|
105
|
+
tm.from_markdown(markdown)
|
|
106
|
+
_, peak_bytes = tracemalloc.get_traced_memory()
|
|
107
|
+
tracemalloc.stop()
|
|
108
|
+
|
|
109
|
+
start = time.perf_counter()
|
|
110
|
+
serialized = doc.to_markdown()
|
|
111
|
+
serialize_seconds = time.perf_counter() - start
|
|
112
|
+
output_bytes = len(serialized.encode("utf-8"))
|
|
113
|
+
|
|
114
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=args.threads) as pool:
|
|
115
|
+
start = time.perf_counter()
|
|
116
|
+
list(pool.map(tm.from_markdown, [markdown] * args.threads))
|
|
117
|
+
threaded_seconds = time.perf_counter() - start
|
|
118
|
+
|
|
119
|
+
print(f"sections: {args.sections}")
|
|
120
|
+
print(f"input: {size_mb:.2f} MiB")
|
|
121
|
+
print(f"output: {mib(output_bytes):.2f} MiB")
|
|
122
|
+
print(f"ast nodes: {node_count:,}")
|
|
123
|
+
print(f"markdown -> Document: {size_mb / parse_seconds:.2f} MiB/s ({parse_seconds:.3f}s)")
|
|
124
|
+
print(f"python peak during parse: {peak_bytes / (1024 * 1024):.2f} MiB")
|
|
125
|
+
print(f"Document -> Markdown: {size_mb / serialize_seconds:.2f} MiB/s ({serialize_seconds:.3f}s)")
|
|
126
|
+
print(f"Document -> dict: {node_count / to_dict_seconds:.0f} nodes/s ({to_dict_seconds:.3f}s)")
|
|
127
|
+
print(f"{args.threads} threaded parses: {(size_mb * args.threads) / threaded_seconds:.2f} MiB/s ({threaded_seconds:.3f}s)")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
if __name__ == "__main__":
|
|
131
|
+
main()
|