markdown-exec 1.8.3__py3-none-any.whl → 1.9.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_exec/__init__.py +7 -1
- markdown_exec/formatters/base.py +53 -3
- markdown_exec/formatters/pyodide.py +11 -2
- markdown_exec/formatters/python.py +1 -1
- markdown_exec/logger.py +20 -7
- markdown_exec/mkdocs_plugin.py +10 -0
- markdown_exec/processors.py +17 -12
- markdown_exec/pyodide.css +1 -0
- {markdown_exec-1.8.3.dist-info → markdown_exec-1.9.0.dist-info}/METADATA +8 -3
- {markdown_exec-1.8.3.dist-info → markdown_exec-1.9.0.dist-info}/RECORD +13 -13
- {markdown_exec-1.8.3.dist-info → markdown_exec-1.9.0.dist-info}/WHEEL +0 -0
- {markdown_exec-1.8.3.dist-info → markdown_exec-1.9.0.dist-info}/entry_points.txt +0 -0
- {markdown_exec-1.8.3.dist-info → markdown_exec-1.9.0.dist-info}/licenses/LICENSE +0 -0
markdown_exec/__init__.py
CHANGED
@@ -8,6 +8,7 @@ Utilities to execute code blocks in Markdown files.
|
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
10
|
|
11
|
+
import os
|
11
12
|
import re
|
12
13
|
from typing import TYPE_CHECKING, Any
|
13
14
|
|
@@ -26,6 +27,7 @@ from markdown_exec.formatters.tree import _format_tree
|
|
26
27
|
|
27
28
|
__all__: list[str] = ["formatter", "validator"]
|
28
29
|
|
30
|
+
MARKDOWN_EXEC_AUTO = [lang.strip() for lang in os.getenv("MARKDOWN_EXEC_AUTO", "").split(",")]
|
29
31
|
|
30
32
|
formatters = {
|
31
33
|
"bash": _format_bash,
|
@@ -63,7 +65,7 @@ def validator(
|
|
63
65
|
Returns:
|
64
66
|
Success or not.
|
65
67
|
"""
|
66
|
-
exec_value = _to_bool(inputs.pop("exec", "no"))
|
68
|
+
exec_value = language in MARKDOWN_EXEC_AUTO or _to_bool(inputs.pop("exec", "no"))
|
67
69
|
if language not in {"tree", "pyodide"} and not exec_value:
|
68
70
|
return False
|
69
71
|
id_value = inputs.pop("id", "")
|
@@ -76,6 +78,8 @@ def validator(
|
|
76
78
|
update_toc_value = _to_bool(inputs.pop("updatetoc", "yes"))
|
77
79
|
tabs_value = inputs.pop("tabs", "|".join(default_tabs))
|
78
80
|
tabs = tuple(_tabs_re.split(tabs_value, maxsplit=1))
|
81
|
+
workdir_value = inputs.pop("workdir", None)
|
82
|
+
width_value = int(inputs.pop("width", "0"))
|
79
83
|
options["id"] = id_value
|
80
84
|
options["id_prefix"] = id_prefix_value
|
81
85
|
options["html"] = html_value
|
@@ -85,6 +89,8 @@ def validator(
|
|
85
89
|
options["session"] = session_value
|
86
90
|
options["update_toc"] = update_toc_value
|
87
91
|
options["tabs"] = tabs
|
92
|
+
options["workdir"] = workdir_value
|
93
|
+
options["width"] = width_value
|
88
94
|
options["extra"] = inputs
|
89
95
|
return True
|
90
96
|
|
markdown_exec/formatters/base.py
CHANGED
@@ -2,8 +2,10 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
+
import os
|
6
|
+
from contextlib import contextmanager
|
5
7
|
from textwrap import indent
|
6
|
-
from typing import TYPE_CHECKING, Any, Callable
|
8
|
+
from typing import TYPE_CHECKING, Any, Callable, Iterator
|
7
9
|
from uuid import uuid4
|
8
10
|
|
9
11
|
from markupsafe import Markup
|
@@ -18,6 +20,47 @@ logger = get_logger(__name__)
|
|
18
20
|
default_tabs = ("Source", "Result")
|
19
21
|
|
20
22
|
|
23
|
+
@contextmanager
|
24
|
+
def working_directory(path: str | None = None) -> Iterator[None]:
|
25
|
+
"""Change the working directory for the duration of the context.
|
26
|
+
|
27
|
+
Parameters:
|
28
|
+
path: The path to change the working directory to.
|
29
|
+
"""
|
30
|
+
if path:
|
31
|
+
old_cwd = os.getcwd()
|
32
|
+
os.chdir(path)
|
33
|
+
try:
|
34
|
+
yield
|
35
|
+
finally:
|
36
|
+
os.chdir(old_cwd)
|
37
|
+
else:
|
38
|
+
yield
|
39
|
+
|
40
|
+
|
41
|
+
@contextmanager
|
42
|
+
def console_width(width: int | None = None) -> Iterator[None]:
|
43
|
+
"""Set the console width for the duration of the context.
|
44
|
+
|
45
|
+
The console width is set using the `COLUMNS` environment variable.
|
46
|
+
|
47
|
+
Parameters:
|
48
|
+
width: The width to set the console to.
|
49
|
+
"""
|
50
|
+
if width:
|
51
|
+
old_width = os.environ.get("COLUMNS", None)
|
52
|
+
os.environ["COLUMNS"] = str(width)
|
53
|
+
try:
|
54
|
+
yield
|
55
|
+
finally:
|
56
|
+
if old_width is None:
|
57
|
+
del os.environ["COLUMNS"]
|
58
|
+
else:
|
59
|
+
os.environ["COLUMNS"] = old_width
|
60
|
+
else:
|
61
|
+
yield
|
62
|
+
|
63
|
+
|
21
64
|
class ExecutionError(Exception):
|
22
65
|
"""Exception raised for errors during execution of a code block.
|
23
66
|
|
@@ -55,6 +98,8 @@ def base_format(
|
|
55
98
|
transform_source: Callable[[str], tuple[str, str]] | None = None,
|
56
99
|
session: str | None = None,
|
57
100
|
update_toc: bool = True,
|
101
|
+
workdir: str | None = None,
|
102
|
+
width: int | None = None,
|
58
103
|
**options: Any,
|
59
104
|
) -> Markup:
|
60
105
|
"""Execute code and return HTML.
|
@@ -77,6 +122,7 @@ def base_format(
|
|
77
122
|
session: A session name, to persist state between executed code blocks.
|
78
123
|
update_toc: Whether to include generated headings
|
79
124
|
into the Markdown table of contents (toc extension).
|
125
|
+
workdir: The working directory to use for the execution.
|
80
126
|
**options: Additional options passed from the formatter.
|
81
127
|
|
82
128
|
Returns:
|
@@ -92,7 +138,8 @@ def base_format(
|
|
92
138
|
source_output = code
|
93
139
|
|
94
140
|
try:
|
95
|
-
|
141
|
+
with working_directory(workdir), console_width(width):
|
142
|
+
output = run(source_input, returncode=returncode, session=session, id=id, **extra)
|
96
143
|
except ExecutionError as error:
|
97
144
|
identifier = id or extra.get("title", "")
|
98
145
|
identifier = identifier and f"'{identifier}' "
|
@@ -105,9 +152,12 @@ def base_format(
|
|
105
152
|
logger.warning(log_message)
|
106
153
|
return markdown.convert(str(error))
|
107
154
|
|
155
|
+
if not output:
|
156
|
+
return Markup()
|
157
|
+
|
108
158
|
if html:
|
109
159
|
if source:
|
110
|
-
placeholder =
|
160
|
+
placeholder = f'<div class="{uuid4()}"></div>'
|
111
161
|
wrapped_output = add_source(
|
112
162
|
source=source_output,
|
113
163
|
location=source,
|
@@ -7,16 +7,21 @@ from typing import TYPE_CHECKING, Any
|
|
7
7
|
if TYPE_CHECKING:
|
8
8
|
from markdown import Markdown
|
9
9
|
|
10
|
+
# All Ace.js themes listed here:
|
11
|
+
# https://github.com/ajaxorg/ace/tree/master/src/theme
|
12
|
+
|
10
13
|
play_emoji = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8 5.14v14l11-7-11-7Z"></path></svg>'
|
11
14
|
clear_emoji = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.14 3c-.51 0-1.02.2-1.41.59L2.59 14.73c-.78.77-.78 2.04 0 2.83L5.03 20h7.66l8.72-8.73c.79-.77.79-2.04 0-2.83l-4.85-4.85c-.39-.39-.91-.59-1.42-.59M17 18l-2 2h7v-2"></path></svg>'
|
12
15
|
|
13
|
-
|
16
|
+
assets = """
|
14
17
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.16.0/ace.js"></script>
|
15
18
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
|
16
19
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/pyodide/v0.23.0/full/pyodide.js"></script>
|
17
20
|
<link title="light" rel="alternate stylesheet" href="https://cdn.jsdelivr.net/npm/highlightjs-themes@1.0.0/tomorrow.min.css" disabled="disabled">
|
18
21
|
<link title="dark" rel="alternate stylesheet" href="https://cdn.jsdelivr.net/npm/highlightjs-themes@1.0.0/tomorrow-night-blue.min.css" disabled="disabled">
|
22
|
+
"""
|
19
23
|
|
24
|
+
template = """
|
20
25
|
<div class="pyodide">
|
21
26
|
<div class="pyodide-editor-bar">
|
22
27
|
<span class="pyodide-bar-item">Editor (session: %(session)s)</span><span id="%(id_prefix)srun" title="Run: press Ctrl-Enter" class="pyodide-bar-item pyodide-clickable"><span class="twemoji">%(play_emoji)s</span> Run</span>
|
@@ -43,6 +48,7 @@ def _format_pyodide(code: str, md: Markdown, session: str, extra: dict, **option
|
|
43
48
|
_counter += 1
|
44
49
|
install = extra.pop("install", "")
|
45
50
|
install = install.split(",") if install else []
|
51
|
+
exclude_assets = extra.pop("assets", "1").lower() in {"0", "false", "no", "off"}
|
46
52
|
theme = extra.pop("theme", "tomorrow,tomorrow_night")
|
47
53
|
if "," not in theme:
|
48
54
|
theme = f"{theme},{theme}"
|
@@ -57,4 +63,7 @@ def _format_pyodide(code: str, md: Markdown, session: str, extra: dict, **option
|
|
57
63
|
"play_emoji": play_emoji,
|
58
64
|
"clear_emoji": clear_emoji,
|
59
65
|
}
|
60
|
-
|
66
|
+
rendered = template % data
|
67
|
+
if exclude_assets:
|
68
|
+
return rendered
|
69
|
+
return assets + rendered
|
@@ -69,7 +69,7 @@ def _run_python(
|
|
69
69
|
|
70
70
|
try:
|
71
71
|
exec_python(code, code_block_id, exec_globals)
|
72
|
-
except Exception as error:
|
72
|
+
except Exception as error:
|
73
73
|
trace = traceback.TracebackException.from_exception(error)
|
74
74
|
for frame in trace.stack:
|
75
75
|
if frame.filename.startswith("<code block: "):
|
markdown_exec/logger.py
CHANGED
@@ -39,22 +39,35 @@ class _Logger:
|
|
39
39
|
_default_logger: Any = logging.getLogger
|
40
40
|
_instances: ClassVar[dict[str, _Logger]] = {}
|
41
41
|
|
42
|
+
# See same code in Griffe project.
|
42
43
|
def __init__(self, name: str) -> None:
|
43
|
-
#
|
44
|
+
# Default logger that can be patched by third-party.
|
44
45
|
self._logger = self.__class__._default_logger(name)
|
45
|
-
# register instance
|
46
|
-
self._instances[name] = self
|
47
46
|
|
48
47
|
def __getattr__(self, name: str) -> Any:
|
49
|
-
#
|
48
|
+
# Forward everything to the logger.
|
50
49
|
return getattr(self._logger, name)
|
51
50
|
|
51
|
+
@classmethod
|
52
|
+
def get(cls, name: str) -> _Logger:
|
53
|
+
"""Get a logger instance.
|
54
|
+
|
55
|
+
Parameters:
|
56
|
+
name: The logger name.
|
57
|
+
|
58
|
+
Returns:
|
59
|
+
The logger instance.
|
60
|
+
"""
|
61
|
+
if name not in cls._instances:
|
62
|
+
cls._instances[name] = cls(name)
|
63
|
+
return cls._instances[name]
|
64
|
+
|
52
65
|
@classmethod
|
53
66
|
def _patch_loggers(cls, get_logger_func: Callable) -> None:
|
54
|
-
#
|
67
|
+
# Patch current instances.
|
55
68
|
for name, instance in cls._instances.items():
|
56
69
|
instance._logger = get_logger_func(name)
|
57
|
-
#
|
70
|
+
# Future instances will be patched as well.
|
58
71
|
cls._default_logger = get_logger_func
|
59
72
|
|
60
73
|
|
@@ -67,7 +80,7 @@ def get_logger(name: str) -> _Logger:
|
|
67
80
|
Returns:
|
68
81
|
The logger.
|
69
82
|
"""
|
70
|
-
return _Logger(name)
|
83
|
+
return _Logger.get(name)
|
71
84
|
|
72
85
|
|
73
86
|
def patch_loggers(get_logger_func: Callable[[str], Any]) -> None:
|
markdown_exec/mkdocs_plugin.py
CHANGED
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any, MutableMapping
|
|
9
9
|
|
10
10
|
from mkdocs.config import config_options
|
11
11
|
from mkdocs.config.base import Config
|
12
|
+
from mkdocs.exceptions import PluginError
|
12
13
|
from mkdocs.plugins import BasePlugin
|
13
14
|
from mkdocs.utils import write_file
|
14
15
|
|
@@ -76,6 +77,15 @@ class MarkdownExecPlugin(BasePlugin[MarkdownExecPluginConfig]):
|
|
76
77
|
Returns:
|
77
78
|
The modified config.
|
78
79
|
"""
|
80
|
+
if "pymdownx.superfences" not in config["markdown_extensions"]:
|
81
|
+
message = "The 'markdown-exec' plugin requires the 'pymdownx.superfences' Markdown extension to work."
|
82
|
+
raise PluginError(message)
|
83
|
+
if self.config.ansi in ("required", True) and not ansi_ok:
|
84
|
+
raise PluginError(
|
85
|
+
"The configuration for the 'markdown-exec' plugin requires "
|
86
|
+
"that it is installed with the 'ansi' extra. "
|
87
|
+
"Install it with 'pip install markdown-exec[ansi]'.",
|
88
|
+
)
|
79
89
|
self.mkdocs_config_dir = os.getenv("MKDOCS_CONFIG_DIR")
|
80
90
|
os.environ["MKDOCS_CONFIG_DIR"] = os.path.dirname(config["config_file_path"])
|
81
91
|
self.languages = self.config.languages
|
markdown_exec/processors.py
CHANGED
@@ -92,9 +92,9 @@ class InsertHeadings(Treeprocessor):
|
|
92
92
|
if match:
|
93
93
|
counter = int(match.group(1))
|
94
94
|
markup: Markup = self.md.htmlStash.rawHtmlBlocks[counter] # type: ignore[assignment]
|
95
|
-
if
|
95
|
+
if headings := self.headings.get(markup):
|
96
96
|
div = Element("div", {"class": "markdown-exec"})
|
97
|
-
div.extend(
|
97
|
+
div.extend(headings)
|
98
98
|
el.append(div)
|
99
99
|
|
100
100
|
|
@@ -104,15 +104,20 @@ class RemoveHeadings(Treeprocessor):
|
|
104
104
|
name = "markdown_exec_remove_headings"
|
105
105
|
|
106
106
|
def run(self, root: Element) -> None: # noqa: D102
|
107
|
+
self._remove_duplicated_headings(root)
|
108
|
+
|
109
|
+
def _remove_duplicated_headings(self, parent: Element) -> None:
|
107
110
|
carry_text = ""
|
108
|
-
for el in reversed(
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
111
|
+
for el in reversed(parent): # Reversed mainly for the ability to mutate during iteration.
|
112
|
+
if el.tag == "div" and el.get("class") == "markdown-exec":
|
113
|
+
# Delete the duplicated headings along with their container, but keep the text (i.e. the actual HTML).
|
114
|
+
carry_text = (el.text or "") + carry_text
|
115
|
+
parent.remove(el)
|
116
|
+
else:
|
117
|
+
if carry_text:
|
118
|
+
el.tail = (el.tail or "") + carry_text
|
116
119
|
carry_text = ""
|
117
|
-
|
118
|
-
|
120
|
+
self._remove_duplicated_headings(el)
|
121
|
+
|
122
|
+
if carry_text:
|
123
|
+
parent.text = (parent.text or "") + carry_text
|
markdown_exec/pyodide.css
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: markdown-exec
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.9.0
|
4
4
|
Summary: Utilities to execute code blocks in Markdown files.
|
5
5
|
Keywords: markdown,python,exec,shell,bash,mkdocs
|
6
6
|
Author-Email: =?utf-8?q?Timoth=C3=A9e_Mazzucotelli?= <dev@pawamoy.fr>
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.10
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
18
19
|
Classifier: Topic :: Documentation
|
19
20
|
Classifier: Topic :: Software Development
|
20
21
|
Classifier: Topic :: Utilities
|
@@ -36,9 +37,9 @@ Description-Content-Type: text/markdown
|
|
36
37
|
# Markdown Exec
|
37
38
|
|
38
39
|
[](https://github.com/pawamoy/markdown-exec/actions?query=workflow%3Aci)
|
39
|
-
[](https://pawamoy.github.io/markdown-exec/)
|
40
41
|
[](https://pypi.org/project/markdown-exec/)
|
41
|
-
[](https://gitpod.io/#https://github.com/pawamoy/markdown-exec)
|
42
43
|
[](https://app.gitter.im/#/room/#markdown-exec:gitter.im)
|
43
44
|
|
44
45
|
Utilities to execute code blocks in Markdown files.
|
@@ -113,6 +114,10 @@ markdown_extensions:
|
|
113
114
|
plugins:
|
114
115
|
- search
|
115
116
|
- markdown-exec
|
117
|
+
|
118
|
+
# SuperFences must still be enabled!
|
119
|
+
markdown_extensions:
|
120
|
+
- pymdownx.superfences
|
116
121
|
```
|
117
122
|
|
118
123
|
We do recommend enabling Markdown Exec with the MkDocs plugin
|
@@ -1,26 +1,26 @@
|
|
1
|
-
markdown_exec-1.
|
2
|
-
markdown_exec-1.
|
3
|
-
markdown_exec-1.
|
4
|
-
markdown_exec-1.
|
5
|
-
markdown_exec/__init__.py,sha256=
|
1
|
+
markdown_exec-1.9.0.dist-info/METADATA,sha256=cDM9KEMiLpxPMIYvTd9GxN_YrTeJqN1mOMqdYlTkeqE,5080
|
2
|
+
markdown_exec-1.9.0.dist-info/WHEEL,sha256=vnE8JVcI2Wz7GRKorsPArnBdnW2SWKWGow5gu5tHlRU,90
|
3
|
+
markdown_exec-1.9.0.dist-info/entry_points.txt,sha256=W-JWRoZzS5TXRcyVnxYmsaVBV4HSl7B_7uTgtaZ-Pms,81
|
4
|
+
markdown_exec-1.9.0.dist-info/licenses/LICENSE,sha256=eZQBcJKqlN0QepmOi0u09hlqKMPFdzWjY6NUWYeJGZs,754
|
5
|
+
markdown_exec/__init__.py,sha256=imnxEGJ4SCay2aeK-IMv5lnSwi7NurpPaoETXJZRsIk,4675
|
6
6
|
markdown_exec/ansi.css,sha256=6PTJxTSsExVgPbMySCuKjih0gr-fdfx2aZ9--u7zWf0,8090
|
7
7
|
markdown_exec/debug.py,sha256=dy0bTd9mTTyTaWuUGNkH0UUdMp2ZThsmGh5q9wt9O0c,2840
|
8
8
|
markdown_exec/formatters/__init__.py,sha256=w8ui1JaGUA5SCyWd2pPAAopQ5y6QYtDvOUHUa7KW2Wg,51
|
9
9
|
markdown_exec/formatters/_exec_python.py,sha256=eVomRZQQl2DySCZIrtqipmBme3kgwIjjRIu8dl22JdE,323
|
10
|
-
markdown_exec/formatters/base.py,sha256=
|
10
|
+
markdown_exec/formatters/base.py,sha256=zrkC2XTRGcU2jF2kIWbk0igZMhkQ--9TfMu1ORKp__I,6095
|
11
11
|
markdown_exec/formatters/bash.py,sha256=qVn6nPqitUuFX72ll00w_x343zl_bMuXrr-Xyox4dJY,886
|
12
12
|
markdown_exec/formatters/console.py,sha256=D2JBIC4MU0ct2MiRCkBO-qo4MiH6Hjy6LXjYAsRmuRA,815
|
13
13
|
markdown_exec/formatters/markdown.py,sha256=pd0akvFGUXrc41NABcHUTTkKFA3k5Ju8im-b3kzOvIw,276
|
14
14
|
markdown_exec/formatters/pycon.py,sha256=F9xpSRKFWsVpGu5XXybtCkMMJ_PAfyd48Qqo1SWl6RA,854
|
15
|
-
markdown_exec/formatters/pyodide.py,sha256=
|
16
|
-
markdown_exec/formatters/python.py,sha256=
|
15
|
+
markdown_exec/formatters/pyodide.py,sha256=fxjYM4Ev2FAiANtGhb7ih9D70Qz3kNH4yJvwz8v7Adw,3169
|
16
|
+
markdown_exec/formatters/python.py,sha256=RmJtPMTJKb-haFsqSZ2nlx0t7Ref63ZhzGVHlteH0Qg,3009
|
17
17
|
markdown_exec/formatters/sh.py,sha256=41kmwFjkyisWYmfJeBMC40kqhEe7sI2E0vvbO9ixjCg,876
|
18
18
|
markdown_exec/formatters/tree.py,sha256=4XU1KaNqChkkNxMYanwy6glTE2uwcY8Mz9jBZiIf3zY,2046
|
19
|
-
markdown_exec/logger.py,sha256=
|
20
|
-
markdown_exec/mkdocs_plugin.py,sha256=
|
21
|
-
markdown_exec/processors.py,sha256=
|
19
|
+
markdown_exec/logger.py,sha256=V8b_D19B0NkZuLr6cBQJnQgHAlz6NyqeENqHKWvTk5M,2422
|
20
|
+
markdown_exec/mkdocs_plugin.py,sha256=2PdhdDceGjgpHrfRuDcvSJHXZGdPKEKtedAbWTmrCu8,5291
|
21
|
+
markdown_exec/processors.py,sha256=sE7ZWz_NisdcOdzQStqzPgvz7qnPkzNy4mqHLy6bugM,4411
|
22
22
|
markdown_exec/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
|
-
markdown_exec/pyodide.css,sha256=
|
23
|
+
markdown_exec/pyodide.css,sha256=7jToXYySNVdX4x9jANwRfjRjYvM6robHibGPWCFzeBQ,871
|
24
24
|
markdown_exec/pyodide.js,sha256=6iL-9xA4b9UeZgsxVYq_BiCE-Bwu58NfYwYCzz9C9j0,3845
|
25
25
|
markdown_exec/rendering.py,sha256=ssWg6U-7Ez45N6sLak-oGD2e55qtEkPY8T8l7h1RbDM,9459
|
26
|
-
markdown_exec-1.
|
26
|
+
markdown_exec-1.9.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|