markdown-exec 1.10.1__py3-none-any.whl → 1.10.2__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 +50 -138
- markdown_exec/_internal/__init__.py +0 -0
- markdown_exec/{debug.py → _internal/debug.py} +13 -15
- markdown_exec/_internal/formatters/__init__.py +1 -0
- markdown_exec/{formatters → _internal/formatters}/_exec_python.py +1 -1
- markdown_exec/_internal/formatters/base.py +191 -0
- markdown_exec/_internal/formatters/bash.py +32 -0
- markdown_exec/_internal/formatters/console.py +29 -0
- markdown_exec/_internal/formatters/markdown.py +11 -0
- markdown_exec/_internal/formatters/pycon.py +26 -0
- markdown_exec/_internal/formatters/pyodide.py +73 -0
- markdown_exec/_internal/formatters/python.py +85 -0
- markdown_exec/_internal/formatters/sh.py +32 -0
- markdown_exec/_internal/formatters/tree.py +60 -0
- markdown_exec/_internal/logger.py +89 -0
- markdown_exec/_internal/main.py +123 -0
- markdown_exec/_internal/mkdocs_plugin.py +143 -0
- markdown_exec/_internal/processors.py +136 -0
- markdown_exec/_internal/rendering.py +280 -0
- markdown_exec/{pyodide.js → assets/pyodide.js} +12 -6
- markdown_exec/formatters/__init__.py +17 -1
- markdown_exec/formatters/base.py +12 -183
- markdown_exec/formatters/bash.py +10 -25
- markdown_exec/formatters/console.py +12 -24
- markdown_exec/formatters/markdown.py +11 -5
- markdown_exec/formatters/pycon.py +12 -24
- markdown_exec/formatters/pyodide.py +12 -65
- markdown_exec/formatters/python.py +11 -79
- markdown_exec/formatters/sh.py +10 -25
- markdown_exec/formatters/tree.py +12 -55
- markdown_exec/logger.py +12 -87
- markdown_exec/mkdocs_plugin.py +11 -135
- markdown_exec/processors.py +12 -118
- markdown_exec/rendering.py +11 -270
- {markdown_exec-1.10.1.dist-info → markdown_exec-1.10.2.dist-info}/METADATA +4 -3
- markdown_exec-1.10.2.dist-info/RECORD +42 -0
- markdown_exec-1.10.2.dist-info/entry_points.txt +7 -0
- markdown_exec-1.10.1.dist-info/RECORD +0 -26
- markdown_exec-1.10.1.dist-info/entry_points.txt +0 -7
- /markdown_exec/{ansi.css → assets/ansi.css} +0 -0
- /markdown_exec/{pyodide.css → assets/pyodide.css} +0 -0
- {markdown_exec-1.10.1.dist-info → markdown_exec-1.10.2.dist-info}/WHEEL +0 -0
- {markdown_exec-1.10.1.dist-info → markdown_exec-1.10.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,280 @@
|
|
1
|
+
# Markdown extensions and helpers.
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from contextlib import contextmanager
|
6
|
+
from functools import cache
|
7
|
+
from textwrap import indent
|
8
|
+
from typing import TYPE_CHECKING, Any
|
9
|
+
|
10
|
+
from markdown import Markdown
|
11
|
+
from markupsafe import Markup
|
12
|
+
|
13
|
+
from markdown_exec._internal.processors import (
|
14
|
+
HeadingReportingTreeprocessor,
|
15
|
+
IdPrependingTreeprocessor,
|
16
|
+
InsertHeadings,
|
17
|
+
RemoveHeadings,
|
18
|
+
)
|
19
|
+
|
20
|
+
if TYPE_CHECKING:
|
21
|
+
from collections.abc import Iterator
|
22
|
+
from xml.etree.ElementTree import Element
|
23
|
+
|
24
|
+
from markdown import Extension
|
25
|
+
|
26
|
+
|
27
|
+
def code_block(language: str, code: str, **options: str) -> str:
|
28
|
+
"""Format code as a code block.
|
29
|
+
|
30
|
+
Parameters:
|
31
|
+
language: The code block language.
|
32
|
+
code: The source code to format.
|
33
|
+
**options: Additional options passed from the source, to add back to the generated code block.
|
34
|
+
|
35
|
+
Returns:
|
36
|
+
The formatted code block.
|
37
|
+
"""
|
38
|
+
opts = " ".join(f'{opt_name}="{opt_value}"' for opt_name, opt_value in options.items())
|
39
|
+
return f"````````{language} {opts}\n{code}\n````````"
|
40
|
+
|
41
|
+
|
42
|
+
def tabbed(*tabs: tuple[str, str]) -> str:
|
43
|
+
"""Format tabs using `pymdownx.tabbed` extension.
|
44
|
+
|
45
|
+
Parameters:
|
46
|
+
*tabs: Tuples of strings: title and text.
|
47
|
+
|
48
|
+
Returns:
|
49
|
+
The formatted tabs.
|
50
|
+
"""
|
51
|
+
parts = []
|
52
|
+
for title, text in tabs:
|
53
|
+
title = title.replace(r"\|", "|").strip() # noqa: PLW2901
|
54
|
+
parts.append(f'=== "{title}"')
|
55
|
+
parts.append(indent(text, prefix=" " * 4))
|
56
|
+
parts.append("")
|
57
|
+
return "\n".join(parts)
|
58
|
+
|
59
|
+
|
60
|
+
def _hide_lines(source: str) -> str:
|
61
|
+
return "\n".join(line for line in source.split("\n") if "markdown-exec: hide" not in line).strip()
|
62
|
+
|
63
|
+
|
64
|
+
def add_source(
|
65
|
+
*,
|
66
|
+
source: str,
|
67
|
+
location: str,
|
68
|
+
output: str,
|
69
|
+
language: str,
|
70
|
+
tabs: tuple[str, str],
|
71
|
+
result: str = "",
|
72
|
+
**extra: str,
|
73
|
+
) -> str:
|
74
|
+
"""Add source code block to the output.
|
75
|
+
|
76
|
+
Parameters:
|
77
|
+
source: The source code block.
|
78
|
+
location: Where to add the source (above, below, tabbed-left, tabbed-right, console).
|
79
|
+
output: The current output.
|
80
|
+
language: The code language.
|
81
|
+
tabs: Tabs titles (if used).
|
82
|
+
result: Syntax to use when concatenating source and result with "console" location.
|
83
|
+
**extra: Extra options added back to source code block.
|
84
|
+
|
85
|
+
Raises:
|
86
|
+
ValueError: When the given location is not supported.
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
The updated output.
|
90
|
+
"""
|
91
|
+
source = _hide_lines(source)
|
92
|
+
if location == "console":
|
93
|
+
return code_block(result or language, source + "\n" + output, **extra)
|
94
|
+
|
95
|
+
source_block = code_block(language, source, **extra)
|
96
|
+
if location == "above":
|
97
|
+
return source_block + "\n\n" + output
|
98
|
+
if location == "below":
|
99
|
+
return output + "\n\n" + source_block
|
100
|
+
if location == "material-block":
|
101
|
+
return source_block + f'\n\n<div class="result" markdown="1" >\n\n{output}\n\n</div>'
|
102
|
+
|
103
|
+
source_tab_title, result_tab_title = tabs
|
104
|
+
if location == "tabbed-left":
|
105
|
+
return tabbed((source_tab_title, source_block), (result_tab_title, output))
|
106
|
+
if location == "tabbed-right":
|
107
|
+
return tabbed((result_tab_title, output), (source_tab_title, source_block))
|
108
|
+
|
109
|
+
raise ValueError(f"unsupported location for sources: {location}")
|
110
|
+
|
111
|
+
|
112
|
+
class MarkdownConfig:
|
113
|
+
"""This class returns a singleton used to store Markdown extensions configuration.
|
114
|
+
|
115
|
+
You don't have to instantiate the singleton yourself:
|
116
|
+
we provide it as [`markdown_config`][markdown_exec.markdown_config].
|
117
|
+
"""
|
118
|
+
|
119
|
+
_singleton: MarkdownConfig | None = None
|
120
|
+
|
121
|
+
def __new__(cls) -> MarkdownConfig: # noqa: PYI034
|
122
|
+
"""Return the singleton instance."""
|
123
|
+
if cls._singleton is None:
|
124
|
+
cls._singleton = super().__new__(cls)
|
125
|
+
return cls._singleton
|
126
|
+
|
127
|
+
def __init__(self) -> None:
|
128
|
+
self.exts: list[str] | None = None
|
129
|
+
"""The Markdown extensions."""
|
130
|
+
self.exts_config: dict[str, dict[str, Any]] | None = None
|
131
|
+
"""The extensions configuration."""
|
132
|
+
|
133
|
+
def save(self, exts: list[str], exts_config: dict[str, dict[str, Any]]) -> None:
|
134
|
+
"""Save Markdown extensions and their configuration.
|
135
|
+
|
136
|
+
Parameters:
|
137
|
+
exts: The Markdown extensions.
|
138
|
+
exts_config: The extensions configuration.
|
139
|
+
"""
|
140
|
+
self.exts = exts
|
141
|
+
self.exts_config = exts_config
|
142
|
+
|
143
|
+
def reset(self) -> None:
|
144
|
+
"""Reset Markdown extensions and their configuration."""
|
145
|
+
self.exts = None
|
146
|
+
self.exts_config = None
|
147
|
+
|
148
|
+
|
149
|
+
markdown_config = MarkdownConfig()
|
150
|
+
"""This object can be used to save the configuration of your Markdown extensions.
|
151
|
+
|
152
|
+
For example, since we provide a MkDocs plugin, we use it to store the configuration
|
153
|
+
that was read from `mkdocs.yml`:
|
154
|
+
|
155
|
+
```python
|
156
|
+
from markdown_exec.rendering import markdown_config
|
157
|
+
|
158
|
+
# ...in relevant events/hooks, access and modify extensions and their configs, then:
|
159
|
+
markdown_config.save(extensions, extensions_config)
|
160
|
+
```
|
161
|
+
|
162
|
+
See the actual event hook: [`on_config`][markdown_exec.MarkdownExecPlugin.on_config].
|
163
|
+
See the [`save`][markdown_exec.MarkdownConfig.save]
|
164
|
+
and [`reset`][markdown_exec.MarkdownConfig.reset] methods.
|
165
|
+
|
166
|
+
Without it, Markdown Exec will rely on the `registeredExtensions` attribute
|
167
|
+
of the original Markdown instance, which does not forward everything
|
168
|
+
that was configured, notably extensions like `tables`. Other extensions
|
169
|
+
such as `attr_list` are visible, but fail to register properly when
|
170
|
+
reusing their instances. It means that the rendered HTML might differ
|
171
|
+
from what you expect (tables not rendered, attribute lists not injected,
|
172
|
+
emojis not working, etc.).
|
173
|
+
"""
|
174
|
+
|
175
|
+
# FIXME: When a heading contains an XML entity such as —,
|
176
|
+
# the entity is stashed and replaced with a placeholder.
|
177
|
+
# The heading therefore contains this placeholder.
|
178
|
+
# When reporting the heading to the upper conversion layer (for the ToC),
|
179
|
+
# the placeholder gets unstashed using the upper Markdown instance
|
180
|
+
# instead of the neste one. If the upper instance doesn't know the placeholder,
|
181
|
+
# nothing happens. But if it knows it, we then get a heading with garbabe/previous
|
182
|
+
# contents within it, messing up the ToC.
|
183
|
+
# We should fix this somehow. In the meantime, the workaround is to avoid
|
184
|
+
# XML entities that get stashed in headings.
|
185
|
+
|
186
|
+
|
187
|
+
@cache
|
188
|
+
def _register_headings_processors(md: Markdown) -> None:
|
189
|
+
md.treeprocessors.register(
|
190
|
+
InsertHeadings(md),
|
191
|
+
InsertHeadings.name,
|
192
|
+
priority=75, # right before markdown.blockprocessors.HashHeaderProcessor
|
193
|
+
)
|
194
|
+
md.treeprocessors.register(
|
195
|
+
RemoveHeadings(md),
|
196
|
+
RemoveHeadings.name,
|
197
|
+
priority=4, # right after toc
|
198
|
+
)
|
199
|
+
|
200
|
+
|
201
|
+
def _mimic(md: Markdown, headings: list[Element], *, update_toc: bool = True) -> Markdown:
|
202
|
+
new_md = Markdown()
|
203
|
+
extensions: list[Extension | str] = markdown_config.exts or md.registeredExtensions # type: ignore[assignment]
|
204
|
+
extensions_config: dict[str, dict[str, Any]] = markdown_config.exts_config or {}
|
205
|
+
new_md.registerExtensions(extensions, extensions_config)
|
206
|
+
new_md.treeprocessors.register(
|
207
|
+
IdPrependingTreeprocessor(md, ""),
|
208
|
+
IdPrependingTreeprocessor.name,
|
209
|
+
priority=4, # right after 'toc' (needed because that extension adds ids to headings)
|
210
|
+
)
|
211
|
+
new_md._original_md = md # type: ignore[attr-defined]
|
212
|
+
|
213
|
+
if update_toc:
|
214
|
+
_register_headings_processors(md)
|
215
|
+
new_md.treeprocessors.register(
|
216
|
+
HeadingReportingTreeprocessor(new_md, headings),
|
217
|
+
HeadingReportingTreeprocessor.name,
|
218
|
+
priority=1, # Close to the end.
|
219
|
+
)
|
220
|
+
|
221
|
+
return new_md
|
222
|
+
|
223
|
+
|
224
|
+
@contextmanager
|
225
|
+
def _id_prefix(md: Markdown, prefix: str | None) -> Iterator[None]:
|
226
|
+
MarkdownConverter.counter += 1
|
227
|
+
id_prepending_processor = md.treeprocessors[IdPrependingTreeprocessor.name]
|
228
|
+
id_prepending_processor.id_prefix = prefix if prefix is not None else f"exec-{MarkdownConverter.counter}--" # type: ignore[attr-defined]
|
229
|
+
try:
|
230
|
+
yield
|
231
|
+
finally:
|
232
|
+
id_prepending_processor.id_prefix = "" # type: ignore[attr-defined]
|
233
|
+
|
234
|
+
|
235
|
+
class MarkdownConverter:
|
236
|
+
"""Helper class to avoid breaking the original Markdown instance state."""
|
237
|
+
|
238
|
+
counter: int = 0
|
239
|
+
"""A counter to generate unique IDs for code blocks."""
|
240
|
+
|
241
|
+
def __init__(self, md: Markdown, *, update_toc: bool = True) -> None:
|
242
|
+
self._md_ref: Markdown = md
|
243
|
+
self._headings: list[Element] = []
|
244
|
+
self._update_toc = update_toc
|
245
|
+
|
246
|
+
@property
|
247
|
+
def _original_md(self) -> Markdown:
|
248
|
+
return getattr(self._md_ref, "_original_md", self._md_ref)
|
249
|
+
|
250
|
+
def _report_headings(self, markup: Markup) -> None:
|
251
|
+
self._original_md.treeprocessors[InsertHeadings.name].headings[markup] = self._headings # type: ignore[attr-defined]
|
252
|
+
self._headings = []
|
253
|
+
|
254
|
+
def convert(self, text: str, stash: dict[str, str] | None = None, id_prefix: str | None = None) -> Markup:
|
255
|
+
"""Convert Markdown text to safe HTML.
|
256
|
+
|
257
|
+
Parameters:
|
258
|
+
text: Markdown text.
|
259
|
+
stash: An HTML stash.
|
260
|
+
|
261
|
+
Returns:
|
262
|
+
Safe HTML.
|
263
|
+
"""
|
264
|
+
md = _mimic(self._original_md, self._headings, update_toc=self._update_toc)
|
265
|
+
|
266
|
+
# convert markdown to html
|
267
|
+
with _id_prefix(md, id_prefix):
|
268
|
+
converted = md.convert(text)
|
269
|
+
|
270
|
+
# restore html from stash
|
271
|
+
for placeholder, stashed in (stash or {}).items():
|
272
|
+
converted = converted.replace(placeholder, stashed)
|
273
|
+
|
274
|
+
markup = Markup(converted) # noqa: S704
|
275
|
+
|
276
|
+
# pass headings to upstream conversion layer
|
277
|
+
if self._update_toc:
|
278
|
+
self._report_headings(markup)
|
279
|
+
|
280
|
+
return markup
|
@@ -22,9 +22,9 @@ async function evaluatePython(pyodide, editor, output, session) {
|
|
22
22
|
try {
|
23
23
|
result = await pyodide.runPythonAsync(code, { globals: getSession(session, pyodide) });
|
24
24
|
} catch (error) {
|
25
|
-
writeOutput(output, error);
|
25
|
+
writeOutput(output, new Option(error.toString()).innerHTML);
|
26
26
|
}
|
27
|
-
if (result) writeOutput(output, result);
|
27
|
+
if (result) writeOutput(output, new Option(result).innerHTML);
|
28
28
|
hljs.highlightElement(output);
|
29
29
|
}
|
30
30
|
|
@@ -91,11 +91,17 @@ async function setupPyodide(idPrefix, install = null, themeLight = 'tomorrow', t
|
|
91
91
|
writeOutput(output, "Initializing...");
|
92
92
|
let pyodide = await pyodidePromise;
|
93
93
|
if (install && install.length) {
|
94
|
-
|
95
|
-
|
96
|
-
|
94
|
+
try {
|
95
|
+
micropip = pyodide.pyimport("micropip");
|
96
|
+
for (const package of install)
|
97
|
+
await micropip.install(package);
|
98
|
+
clearOutput(output);
|
99
|
+
} catch (error) {
|
100
|
+
clearOutput(output);
|
101
|
+
writeOutput(output, `Could not install one or more packages: ${install.join(", ")}\n`);
|
102
|
+
writeOutput(output, new Option(error.toString()).innerHTML);
|
103
|
+
}
|
97
104
|
}
|
98
|
-
clearOutput(output);
|
99
105
|
run.onclick = () => evaluatePython(pyodide, editor, output, session);
|
100
106
|
clear.onclick = () => clearOutput(output);
|
101
107
|
output.parentElement.parentElement.addEventListener("keydown", (event) => {
|
@@ -1 +1,17 @@
|
|
1
|
-
"""
|
1
|
+
"""Deprecated. Import from `markdown_exec` directly."""
|
2
|
+
|
3
|
+
# YORE: Bump 2: Remove file.
|
4
|
+
|
5
|
+
import warnings
|
6
|
+
from typing import Any
|
7
|
+
|
8
|
+
from markdown_exec._internal import formatters
|
9
|
+
|
10
|
+
|
11
|
+
def __getattr__(name: str) -> Any:
|
12
|
+
warnings.warn(
|
13
|
+
"Importing from `markdown_exec.formatters` is deprecated. Import from `markdown_exec` directly.",
|
14
|
+
DeprecationWarning,
|
15
|
+
stacklevel=2,
|
16
|
+
)
|
17
|
+
return getattr(formatters, name)
|
markdown_exec/formatters/base.py
CHANGED
@@ -1,188 +1,17 @@
|
|
1
|
-
"""
|
1
|
+
"""Deprecated. Import from `markdown_exec` directly."""
|
2
2
|
|
3
|
-
|
3
|
+
# YORE: Bump 2: Remove file.
|
4
4
|
|
5
|
-
import
|
6
|
-
from
|
7
|
-
from textwrap import indent
|
8
|
-
from typing import TYPE_CHECKING, Any, Callable
|
9
|
-
from uuid import uuid4
|
5
|
+
import warnings
|
6
|
+
from typing import Any
|
10
7
|
|
11
|
-
from
|
8
|
+
from markdown_exec._internal.formatters import base
|
12
9
|
|
13
|
-
from markdown_exec.logger import get_logger
|
14
|
-
from markdown_exec.rendering import MarkdownConverter, add_source, code_block
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@contextmanager
|
26
|
-
def working_directory(path: str | None = None) -> Iterator[None]:
|
27
|
-
"""Change the working directory for the duration of the context.
|
28
|
-
|
29
|
-
Parameters:
|
30
|
-
path: The path to change the working directory to.
|
31
|
-
"""
|
32
|
-
if path:
|
33
|
-
old_cwd = os.getcwd()
|
34
|
-
os.chdir(path)
|
35
|
-
try:
|
36
|
-
yield
|
37
|
-
finally:
|
38
|
-
os.chdir(old_cwd)
|
39
|
-
else:
|
40
|
-
yield
|
41
|
-
|
42
|
-
|
43
|
-
@contextmanager
|
44
|
-
def console_width(width: int | None = None) -> Iterator[None]:
|
45
|
-
"""Set the console width for the duration of the context.
|
46
|
-
|
47
|
-
The console width is set using the `COLUMNS` environment variable.
|
48
|
-
|
49
|
-
Parameters:
|
50
|
-
width: The width to set the console to.
|
51
|
-
"""
|
52
|
-
if width:
|
53
|
-
old_width = os.environ.get("COLUMNS", None)
|
54
|
-
os.environ["COLUMNS"] = str(width)
|
55
|
-
try:
|
56
|
-
yield
|
57
|
-
finally:
|
58
|
-
if old_width is None:
|
59
|
-
del os.environ["COLUMNS"]
|
60
|
-
else:
|
61
|
-
os.environ["COLUMNS"] = old_width
|
62
|
-
else:
|
63
|
-
yield
|
64
|
-
|
65
|
-
|
66
|
-
class ExecutionError(Exception):
|
67
|
-
"""Exception raised for errors during execution of a code block.
|
68
|
-
|
69
|
-
Attributes:
|
70
|
-
message: The exception message.
|
71
|
-
returncode: The code returned by the execution of the code block.
|
72
|
-
"""
|
73
|
-
|
74
|
-
def __init__(self, message: str, returncode: int | None = None) -> None: # noqa: D107
|
75
|
-
super().__init__(message)
|
76
|
-
self.returncode = returncode
|
77
|
-
|
78
|
-
|
79
|
-
def _format_log_details(details: str, *, strip_fences: bool = False) -> str:
|
80
|
-
if strip_fences:
|
81
|
-
lines = details.split("\n")
|
82
|
-
if lines[0].startswith("```") and lines[-1].startswith("```"):
|
83
|
-
details = "\n".join(lines[1:-1])
|
84
|
-
return indent(details, " " * 2)
|
85
|
-
|
86
|
-
|
87
|
-
def base_format(
|
88
|
-
*,
|
89
|
-
language: str,
|
90
|
-
run: Callable,
|
91
|
-
code: str,
|
92
|
-
md: Markdown,
|
93
|
-
html: bool = False,
|
94
|
-
source: str = "",
|
95
|
-
result: str = "",
|
96
|
-
tabs: tuple[str, str] = default_tabs,
|
97
|
-
id: str = "", # noqa: A002
|
98
|
-
id_prefix: str | None = None,
|
99
|
-
returncode: int = 0,
|
100
|
-
transform_source: Callable[[str], tuple[str, str]] | None = None,
|
101
|
-
session: str | None = None,
|
102
|
-
update_toc: bool = True,
|
103
|
-
workdir: str | None = None,
|
104
|
-
width: int | None = None,
|
105
|
-
**options: Any,
|
106
|
-
) -> Markup:
|
107
|
-
"""Execute code and return HTML.
|
108
|
-
|
109
|
-
Parameters:
|
110
|
-
language: The code language.
|
111
|
-
run: Function that runs code and returns output.
|
112
|
-
code: The code to execute.
|
113
|
-
md: The Markdown instance.
|
114
|
-
html: Whether to inject output as HTML directly, without rendering.
|
115
|
-
source: Whether to show source as well, and where.
|
116
|
-
result: If provided, use as language to format result in a code block.
|
117
|
-
tabs: Titles of tabs (if used).
|
118
|
-
id: An optional ID for the code block (useful when warning about errors).
|
119
|
-
id_prefix: A string used to prefix HTML ids in the generated HTML.
|
120
|
-
returncode: The expected exit code.
|
121
|
-
transform_source: An optional callable that returns transformed versions of the source.
|
122
|
-
The input source is the one that is ran, the output source is the one that is
|
123
|
-
rendered (when the source option is enabled).
|
124
|
-
session: A session name, to persist state between executed code blocks.
|
125
|
-
update_toc: Whether to include generated headings
|
126
|
-
into the Markdown table of contents (toc extension).
|
127
|
-
workdir: The working directory to use for the execution.
|
128
|
-
**options: Additional options passed from the formatter.
|
129
|
-
|
130
|
-
Returns:
|
131
|
-
HTML contents.
|
132
|
-
"""
|
133
|
-
markdown = MarkdownConverter(md, update_toc=update_toc)
|
134
|
-
extra = options.get("extra", {})
|
135
|
-
|
136
|
-
if transform_source:
|
137
|
-
source_input, source_output = transform_source(code)
|
138
|
-
else:
|
139
|
-
source_input = code
|
140
|
-
source_output = code
|
141
|
-
|
142
|
-
try:
|
143
|
-
with working_directory(workdir), console_width(width):
|
144
|
-
output = run(source_input, returncode=returncode, session=session, id=id, **extra)
|
145
|
-
except ExecutionError as error:
|
146
|
-
identifier = id or extra.get("title", "")
|
147
|
-
identifier = identifier and f"'{identifier}' "
|
148
|
-
exit_message = "errors" if error.returncode is None else f"unexpected code {error.returncode}"
|
149
|
-
log_message = (
|
150
|
-
f"Execution of {language} code block {identifier}exited with {exit_message}\n\n"
|
151
|
-
f"Code block is:\n\n{_format_log_details(source_input)}\n\n"
|
152
|
-
f"Output is:\n\n{_format_log_details(str(error), strip_fences=True)}\n"
|
153
|
-
)
|
154
|
-
logger.warning(log_message)
|
155
|
-
return markdown.convert(str(error))
|
156
|
-
|
157
|
-
if not output and not source:
|
158
|
-
return Markup()
|
159
|
-
|
160
|
-
if html:
|
161
|
-
if source:
|
162
|
-
placeholder = f'<div class="{uuid4()}"></div>'
|
163
|
-
wrapped_output = add_source(
|
164
|
-
source=source_output,
|
165
|
-
location=source,
|
166
|
-
output=placeholder,
|
167
|
-
language=language,
|
168
|
-
tabs=tabs,
|
169
|
-
**extra,
|
170
|
-
)
|
171
|
-
return markdown.convert(wrapped_output, stash={placeholder: output})
|
172
|
-
return Markup(output)
|
173
|
-
|
174
|
-
wrapped_output = output
|
175
|
-
if result and source != "console":
|
176
|
-
wrapped_output = code_block(result, output)
|
177
|
-
if source:
|
178
|
-
wrapped_output = add_source(
|
179
|
-
source=source_output,
|
180
|
-
location=source,
|
181
|
-
output=wrapped_output,
|
182
|
-
language=language,
|
183
|
-
tabs=tabs,
|
184
|
-
result=result,
|
185
|
-
**extra,
|
186
|
-
)
|
187
|
-
prefix = id_prefix if id_prefix is not None else (f"{id}-" if id else None)
|
188
|
-
return markdown.convert(wrapped_output, id_prefix=prefix)
|
11
|
+
def __getattr__(name: str) -> Any:
|
12
|
+
warnings.warn(
|
13
|
+
"Importing from `markdown_exec.formatters.base` is deprecated. Import from `markdown_exec` directly.",
|
14
|
+
DeprecationWarning,
|
15
|
+
stacklevel=2,
|
16
|
+
)
|
17
|
+
return getattr(base, name)
|
markdown_exec/formatters/bash.py
CHANGED
@@ -1,32 +1,17 @@
|
|
1
|
-
"""
|
1
|
+
"""Deprecated. Import from `markdown_exec` directly."""
|
2
2
|
|
3
|
-
|
3
|
+
# YORE: Bump 2: Remove file.
|
4
4
|
|
5
|
-
import
|
5
|
+
import warnings
|
6
6
|
from typing import Any
|
7
7
|
|
8
|
-
from markdown_exec.formatters
|
9
|
-
from markdown_exec.rendering import code_block
|
8
|
+
from markdown_exec._internal.formatters import bash
|
10
9
|
|
11
10
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
**extra: str,
|
18
|
-
) -> str:
|
19
|
-
process = subprocess.run( # noqa: S603
|
20
|
-
["bash", "-c", code], # noqa: S607
|
21
|
-
stdout=subprocess.PIPE,
|
22
|
-
stderr=subprocess.STDOUT,
|
23
|
-
text=True,
|
24
|
-
check=False,
|
11
|
+
def __getattr__(name: str) -> Any:
|
12
|
+
warnings.warn(
|
13
|
+
"Importing from `markdown_exec.formatters.bash` is deprecated. Import from `markdown_exec` directly.",
|
14
|
+
DeprecationWarning,
|
15
|
+
stacklevel=2,
|
25
16
|
)
|
26
|
-
|
27
|
-
raise ExecutionError(code_block("sh", process.stdout, **extra), process.returncode)
|
28
|
-
return process.stdout
|
29
|
-
|
30
|
-
|
31
|
-
def _format_bash(**kwargs: Any) -> str:
|
32
|
-
return base_format(language="bash", run=_run_bash, **kwargs)
|
17
|
+
return getattr(bash, name)
|
@@ -1,29 +1,17 @@
|
|
1
|
-
"""
|
1
|
+
"""Deprecated. Import from `markdown_exec` directly."""
|
2
2
|
|
3
|
-
|
3
|
+
# YORE: Bump 2: Remove file.
|
4
4
|
|
5
|
-
import
|
6
|
-
from typing import
|
5
|
+
import warnings
|
6
|
+
from typing import Any
|
7
7
|
|
8
|
-
from markdown_exec.formatters
|
9
|
-
from markdown_exec.formatters.sh import _run_sh
|
10
|
-
from markdown_exec.logger import get_logger
|
8
|
+
from markdown_exec._internal.formatters import console
|
11
9
|
|
12
|
-
if TYPE_CHECKING:
|
13
|
-
from markupsafe import Markup
|
14
10
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
if prompt in {"$ ", "% "}:
|
23
|
-
sh_lines.append(line[2:])
|
24
|
-
sh_code = "\n".join(sh_lines)
|
25
|
-
return sh_code, textwrap.indent(sh_code, prompt)
|
26
|
-
|
27
|
-
|
28
|
-
def _format_console(**kwargs: Any) -> Markup:
|
29
|
-
return base_format(language="console", run=_run_sh, transform_source=_transform_source, **kwargs)
|
11
|
+
def __getattr__(name: str) -> Any:
|
12
|
+
warnings.warn(
|
13
|
+
"Importing from `markdown_exec.formatters.console` is deprecated. Import from `markdown_exec` directly.",
|
14
|
+
DeprecationWarning,
|
15
|
+
stacklevel=2,
|
16
|
+
)
|
17
|
+
return getattr(console, name)
|
@@ -1,11 +1,17 @@
|
|
1
|
-
"""
|
1
|
+
"""Deprecated. Import from `markdown_exec` directly."""
|
2
2
|
|
3
|
-
|
3
|
+
# YORE: Bump 2: Remove file.
|
4
4
|
|
5
|
+
import warnings
|
5
6
|
from typing import Any
|
6
7
|
|
7
|
-
from markdown_exec.formatters
|
8
|
+
from markdown_exec._internal.formatters import markdown
|
8
9
|
|
9
10
|
|
10
|
-
def
|
11
|
-
|
11
|
+
def __getattr__(name: str) -> Any:
|
12
|
+
warnings.warn(
|
13
|
+
"Importing from `markdown_exec.formatters.markdown` is deprecated. Import from `markdown_exec` directly.",
|
14
|
+
DeprecationWarning,
|
15
|
+
stacklevel=2,
|
16
|
+
)
|
17
|
+
return getattr(markdown, name)
|
@@ -1,29 +1,17 @@
|
|
1
|
-
"""
|
1
|
+
"""Deprecated. Import from `markdown_exec` directly."""
|
2
2
|
|
3
|
-
|
3
|
+
# YORE: Bump 2: Remove file.
|
4
4
|
|
5
|
-
|
5
|
+
import warnings
|
6
|
+
from typing import Any
|
6
7
|
|
7
|
-
from markdown_exec.formatters
|
8
|
-
from markdown_exec.formatters.python import _run_python
|
9
|
-
from markdown_exec.logger import get_logger
|
8
|
+
from markdown_exec._internal.formatters import pycon
|
10
9
|
|
11
|
-
if TYPE_CHECKING:
|
12
|
-
from markupsafe import Markup
|
13
10
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
if line.startswith((">>> ", "... ")):
|
22
|
-
pycon_lines.append(line)
|
23
|
-
python_lines.append(line[4:])
|
24
|
-
python_code = "\n".join(python_lines)
|
25
|
-
return python_code, "\n".join(pycon_lines)
|
26
|
-
|
27
|
-
|
28
|
-
def _format_pycon(**kwargs: Any) -> Markup:
|
29
|
-
return base_format(language="pycon", run=_run_python, transform_source=_transform_source, **kwargs)
|
11
|
+
def __getattr__(name: str) -> Any:
|
12
|
+
warnings.warn(
|
13
|
+
"Importing from `markdown_exec.formatters.pycon` is deprecated. Import from `markdown_exec` directly.",
|
14
|
+
DeprecationWarning,
|
15
|
+
stacklevel=2,
|
16
|
+
)
|
17
|
+
return getattr(pycon, name)
|