markdown-exec 1.9.0__tar.gz → 1.9.2__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.
Files changed (35) hide show
  1. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/PKG-INFO +1 -1
  2. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/pyproject.toml +2 -1
  3. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/formatters/base.py +1 -1
  4. markdown_exec-1.9.2/tests/__init__.py +7 -0
  5. markdown_exec-1.9.2/tests/conftest.py +28 -0
  6. markdown_exec-1.9.2/tests/test_base_formatter.py +126 -0
  7. markdown_exec-1.9.2/tests/test_converter.py +76 -0
  8. markdown_exec-1.9.2/tests/test_headings.py +30 -0
  9. markdown_exec-1.9.2/tests/test_python.py +231 -0
  10. markdown_exec-1.9.2/tests/test_shell.py +87 -0
  11. markdown_exec-1.9.2/tests/test_toc.py +89 -0
  12. markdown_exec-1.9.2/tests/test_tree.py +25 -0
  13. markdown_exec-1.9.2/tests/test_validator.py +38 -0
  14. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/LICENSE +0 -0
  15. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/README.md +0 -0
  16. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/__init__.py +0 -0
  17. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/ansi.css +0 -0
  18. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/debug.py +0 -0
  19. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/formatters/__init__.py +0 -0
  20. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/formatters/_exec_python.py +0 -0
  21. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/formatters/bash.py +0 -0
  22. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/formatters/console.py +0 -0
  23. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/formatters/markdown.py +0 -0
  24. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/formatters/pycon.py +0 -0
  25. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/formatters/pyodide.py +0 -0
  26. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/formatters/python.py +0 -0
  27. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/formatters/sh.py +0 -0
  28. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/formatters/tree.py +0 -0
  29. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/logger.py +0 -0
  30. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/mkdocs_plugin.py +0 -0
  31. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/processors.py +0 -0
  32. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/py.typed +0 -0
  33. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/pyodide.css +0 -0
  34. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/pyodide.js +0 -0
  35. {markdown_exec-1.9.0 → markdown_exec-1.9.2}/src/markdown_exec/rendering.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: markdown-exec
3
- Version: 1.9.0
3
+ Version: 1.9.2
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>
@@ -41,7 +41,7 @@ classifiers = [
41
41
  dependencies = [
42
42
  "pymdown-extensions>=9",
43
43
  ]
44
- version = "1.9.0"
44
+ version = "1.9.2"
45
45
 
46
46
  [project.license]
47
47
  text = "ISC"
@@ -72,6 +72,7 @@ package-dir = "src"
72
72
  editable-backend = "editables"
73
73
  source-includes = [
74
74
  "share",
75
+ "tests",
75
76
  ]
76
77
 
77
78
  [tool.pdm.build.wheel-data]
@@ -152,7 +152,7 @@ def base_format(
152
152
  logger.warning(log_message)
153
153
  return markdown.convert(str(error))
154
154
 
155
- if not output:
155
+ if not output and not source:
156
156
  return Markup()
157
157
 
158
158
  if html:
@@ -0,0 +1,7 @@
1
+ """Tests suite for `markdown_exec`."""
2
+
3
+ from pathlib import Path
4
+
5
+ TESTS_DIR = Path(__file__).parent
6
+ TMP_DIR = TESTS_DIR / "tmp"
7
+ FIXTURES_DIR = TESTS_DIR / "fixtures"
@@ -0,0 +1,28 @@
1
+ """Configuration for the pytest test suite."""
2
+
3
+ import pytest
4
+ from markdown import Markdown
5
+
6
+ from markdown_exec import formatter, formatters, validator
7
+
8
+
9
+ @pytest.fixture()
10
+ def md() -> Markdown:
11
+ """Return a Markdown instance.
12
+
13
+ Returns:
14
+ Markdown instance.
15
+ """
16
+ fences = [
17
+ {
18
+ "name": language,
19
+ "class": language,
20
+ "validator": validator,
21
+ "format": formatter,
22
+ }
23
+ for language in formatters
24
+ ]
25
+ return Markdown(
26
+ extensions=["pymdownx.superfences", "pymdownx.tabbed"],
27
+ extension_configs={"pymdownx.superfences": {"custom_fences": fences}},
28
+ )
@@ -0,0 +1,126 @@
1
+ """Tests for the base formatter."""
2
+
3
+ import subprocess
4
+
5
+ import pytest
6
+ from markdown import Markdown
7
+
8
+ from markdown_exec.formatters.base import base_format
9
+
10
+
11
+ def test_no_p_around_html(md: Markdown) -> None:
12
+ """Assert HTML isn't wrapped in a `p` tag.
13
+
14
+ Parameters:
15
+ md: A Markdown instance (fixture).
16
+ """
17
+ code = "<pre><code>hello</code></pre>"
18
+ html = base_format(
19
+ language="whatever",
20
+ run=lambda code, **_: code,
21
+ code=code,
22
+ md=md,
23
+ html=True,
24
+ )
25
+ assert html == code
26
+
27
+
28
+ @pytest.mark.parametrize("html", [True, False])
29
+ def test_render_source(md: Markdown, html: bool) -> None:
30
+ """Assert source is rendered.
31
+
32
+ Parameters:
33
+ md: A Markdown instance (fixture).
34
+ html: Whether output is HTML or not.
35
+ """
36
+ markup = base_format(
37
+ language="python",
38
+ run=lambda code, **_: code,
39
+ code="hello",
40
+ md=md,
41
+ html=html,
42
+ source="tabbed-left",
43
+ )
44
+ assert "Source" in markup
45
+
46
+
47
+ def test_render_console_plus_ansi_result(md: Markdown) -> None:
48
+ """Assert we can render source as console style with `ansi` highlight.
49
+
50
+ Parameters:
51
+ md: A Markdown instance (fixture).
52
+ """
53
+ markup = base_format(
54
+ language="bash",
55
+ run=lambda code, **_: code,
56
+ code="echo -e '\033[31mhello'",
57
+ md=md,
58
+ html=False,
59
+ source="console",
60
+ result="ansi",
61
+ )
62
+ assert "<code>ansi" in markup
63
+
64
+
65
+ def test_dont_render_anything_if_output_is_empty(md: Markdown) -> None:
66
+ """Assert nothing is rendered if output is empty.
67
+
68
+ Parameters:
69
+ md: A Markdown instance (fixture).
70
+ """
71
+ markup = base_format(
72
+ language="bash",
73
+ run=lambda code, **_: "",
74
+ code="whatever",
75
+ md=md,
76
+ )
77
+ assert not markup
78
+
79
+
80
+ def test_render_source_even_if_output_is_empty(md: Markdown) -> None:
81
+ """Assert source is rendered even if output is empty.
82
+
83
+ Parameters:
84
+ md: A Markdown instance (fixture).
85
+ """
86
+ markup = base_format(
87
+ language="bash",
88
+ run=lambda code, **_: "",
89
+ code="whatever",
90
+ md=md,
91
+ source="tabbed-left",
92
+ )
93
+ assert "Source" in markup
94
+
95
+
96
+ def test_changing_working_directory(md: Markdown) -> None:
97
+ """Assert we can change the working directory with `workdir`.
98
+
99
+ Parameters:
100
+ md: A Markdown instance (fixture).
101
+ """
102
+ markup = base_format(
103
+ language="python",
104
+ run=lambda code, **_: subprocess.check_output(code, shell=True, text=True), # noqa: S602
105
+ code="pwd",
106
+ md=md,
107
+ workdir="/",
108
+ )
109
+ assert markup == "<p>/</p>"
110
+
111
+
112
+ def test_console_width(md: Markdown) -> None:
113
+ """Assert we can change the console width with `width`.
114
+
115
+ Parameters:
116
+ md: A Markdown instance (fixture).
117
+ """
118
+ for width in (10, 1000):
119
+ markup = base_format(
120
+ language="bash",
121
+ run=lambda code, **_: subprocess.check_output(code, shell=True, text=True), # noqa: S602,
122
+ code="echo width: $COLUMNS",
123
+ md=md,
124
+ width=width,
125
+ )
126
+ assert f"width: {width}" in markup
@@ -0,0 +1,76 @@
1
+ """Tests for the Markdown converter."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from textwrap import dedent
7
+ from typing import TYPE_CHECKING
8
+
9
+ import pytest
10
+ from markdown.extensions.toc import TocExtension
11
+
12
+ from markdown_exec.rendering import MarkdownConfig, markdown_config
13
+
14
+ if TYPE_CHECKING:
15
+ from markdown import Markdown
16
+
17
+
18
+ def test_rendering_nested_blocks(md: Markdown) -> None:
19
+ """Assert nested blocks are properly handled.
20
+
21
+ Parameters:
22
+ md: A Markdown instance (fixture).
23
+ """
24
+ html = md.convert(
25
+ dedent(
26
+ """
27
+ ````md exec="1"
28
+ ```python exec="1"
29
+ print("**Bold!**")
30
+ ```
31
+ ````
32
+ """,
33
+ ),
34
+ )
35
+ assert html == "<p><strong>Bold!</strong></p>"
36
+
37
+
38
+ def test_instantiating_config_singleton() -> None:
39
+ """Assert that the Markdown config instances act as a singleton."""
40
+ assert MarkdownConfig() is markdown_config
41
+ markdown_config.save([], {})
42
+ markdown_config.reset()
43
+
44
+
45
+ @pytest.mark.parametrize(
46
+ ("id", "id_prefix", "expected"),
47
+ [
48
+ ("", None, 'id="exec-\\d+--heading"'),
49
+ ("", "", 'id="heading"'),
50
+ ("", "some-prefix-", 'id="some-prefix-heading"'),
51
+ ("some-id", None, 'id="some-id-heading"'),
52
+ ("some-id", "", 'id="heading"'),
53
+ ("some-id", "some-prefix-", 'id="some-prefix-heading"'),
54
+ ],
55
+ )
56
+ def test_prefixing_headings(md: Markdown, id: str, id_prefix: str | None, expected: str) -> None: # noqa: A002
57
+ """Assert that we prefix headings as specified.
58
+
59
+ Parameters:
60
+ md: A Markdown instance (fixture).
61
+ id: The code block id.
62
+ id_prefix: The code block id prefix.
63
+ expected: The id we expect to find in the HTML.
64
+ """
65
+ TocExtension().extendMarkdown(md)
66
+ prefix = f'idprefix="{id_prefix}"' if id_prefix is not None else ""
67
+ html = md.convert(
68
+ dedent(
69
+ f"""
70
+ ```python exec="1" id="{id}" {prefix}
71
+ print("# HEADING")
72
+ ```
73
+ """,
74
+ ),
75
+ )
76
+ assert re.search(expected, html)
@@ -0,0 +1,30 @@
1
+ """Tests for headings."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from textwrap import dedent
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from markdown import Markdown
10
+
11
+
12
+ def test_headings_removal(md: Markdown) -> None:
13
+ """Headings should leave no trace behind.
14
+
15
+ Parameters:
16
+ md: A Markdown instance (fixture).
17
+ """
18
+ html = md.convert(
19
+ dedent(
20
+ """
21
+ === "File layout"
22
+
23
+ ```tree
24
+ ./
25
+ hello.md
26
+ ```
27
+ """,
28
+ ),
29
+ )
30
+ assert 'class="markdown-exec"' not in html
@@ -0,0 +1,231 @@
1
+ """Tests for the Python formatters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from textwrap import dedent
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ import pytest
11
+ from markdown import Markdown
12
+
13
+
14
+ def test_output_markdown(md: Markdown) -> None:
15
+ """Assert Markdown is converted to HTML.
16
+
17
+ Parameters:
18
+ md: A Markdown instance (fixture).
19
+ """
20
+ html = md.convert(
21
+ dedent(
22
+ """
23
+ ```python exec="yes"
24
+ print("**Bold!**")
25
+ ```
26
+ """,
27
+ ),
28
+ )
29
+ assert html == "<p><strong>Bold!</strong></p>"
30
+
31
+
32
+ def test_output_html(md: Markdown) -> None:
33
+ """Assert HTML is injected as is.
34
+
35
+ Parameters:
36
+ md: A Markdown instance (fixture).
37
+ """
38
+ html = md.convert(
39
+ dedent(
40
+ """
41
+ ```python exec="yes" html="yes"
42
+ print("**Bold!**")
43
+ ```
44
+ """,
45
+ ),
46
+ )
47
+ assert html == "<p>**Bold!**\n</p>"
48
+
49
+
50
+ def test_error_raised(md: Markdown, caplog: pytest.LogCaptureFixture) -> None:
51
+ """Assert errors properly log a warning and return a formatted traceback.
52
+
53
+ Parameters:
54
+ md: A Markdown instance (fixture).
55
+ caplog: Pytest fixture to capture logs.
56
+ """
57
+ html = md.convert(
58
+ dedent(
59
+ """
60
+ ```python exec="yes"
61
+ raise ValueError("oh no!")
62
+ ```
63
+ """,
64
+ ),
65
+ )
66
+ assert "Traceback" in html
67
+ assert "ValueError" in html
68
+ assert "oh no!" in html
69
+ assert "Execution of python code block exited with errors" in caplog.text
70
+
71
+
72
+ def test_can_print_non_string_objects(md: Markdown) -> None:
73
+ """Assert we can print non-string objects.
74
+
75
+ Parameters:
76
+ md: A Markdown instance (fixture).
77
+ """
78
+ html = md.convert(
79
+ dedent(
80
+ """
81
+ ```python exec="yes"
82
+ class NonString:
83
+ def __str__(self):
84
+ return "string"
85
+
86
+ nonstring = NonString()
87
+ print(nonstring, nonstring)
88
+ ```
89
+ """,
90
+ ),
91
+ )
92
+ assert "Traceback" not in html
93
+
94
+
95
+ def test_sessions(md: Markdown) -> None:
96
+ """Assert sessions can be reused.
97
+
98
+ Parameters:
99
+ md: A Markdown instance (fixture).
100
+ """
101
+ html = md.convert(
102
+ dedent(
103
+ """
104
+ ```python exec="1" session="a"
105
+ a = 1
106
+ ```
107
+
108
+ ```pycon exec="1" session="b"
109
+ >>> b = 2
110
+ ```
111
+
112
+ ```pycon exec="1" session="a"
113
+ >>> print(f"a = {a}")
114
+ >>> try:
115
+ ... print(b)
116
+ ... except NameError:
117
+ ... print("ok")
118
+ ... else:
119
+ ... print("ko")
120
+ ```
121
+
122
+ ```python exec="1" session="b"
123
+ print(f"b = {b}")
124
+ try:
125
+ print(a)
126
+ except NameError:
127
+ print("ok")
128
+ else:
129
+ print("ko")
130
+ ```
131
+ """,
132
+ ),
133
+ )
134
+ assert "a = 1" in html
135
+ assert "b = 2" in html
136
+ assert "ok" in html
137
+ assert "ko" not in html
138
+
139
+
140
+ def test_reporting_errors_in_sessions(md: Markdown, caplog: pytest.LogCaptureFixture) -> None:
141
+ """Assert errors and source lines are correctly reported across sessions.
142
+
143
+ Parameters:
144
+ md: A Markdown instance (fixture).
145
+ caplog: Pytest fixture to capture logs.
146
+ """
147
+ html = md.convert(
148
+ dedent(
149
+ """
150
+ ```python exec="1" session="a"
151
+ def fraise():
152
+ raise RuntimeError("strawberry")
153
+ ```
154
+
155
+ ```python exec="1" session="a"
156
+ print("hello")
157
+ fraise()
158
+ ```
159
+ """,
160
+ ),
161
+ )
162
+ assert "Traceback" in html
163
+ assert "strawberry" in html
164
+ assert "fraise()" in caplog.text
165
+ assert 'raise RuntimeError("strawberry")' in caplog.text
166
+
167
+
168
+ def test_removing_output_from_pycon_code(md: Markdown) -> None:
169
+ """Assert output lines are removed from pycon snippets.
170
+
171
+ Parameters:
172
+ md: A Markdown instance (fixture).
173
+ """
174
+ html = md.convert(
175
+ dedent(
176
+ """
177
+ ```pycon exec="1" source="console"
178
+ >>> print("ok")
179
+ ko
180
+ ```
181
+ """,
182
+ ),
183
+ )
184
+ assert "ok" in html
185
+ assert "ko" not in html
186
+
187
+
188
+ def test_functions_have_a_module_attribute(md: Markdown) -> None:
189
+ """Assert functions have a `__module__` attribute.
190
+
191
+ Parameters:
192
+ md: A Markdown instance (fixture).
193
+ """
194
+ html = md.convert(
195
+ dedent(
196
+ """
197
+ ```python exec="1"
198
+ def func():
199
+ pass
200
+
201
+ print(f"`{func.__module__}`")
202
+ ```
203
+ """,
204
+ ),
205
+ )
206
+ assert "_code_block_n" in html
207
+
208
+
209
+ def test_future_annotations_do_not_leak_into_user_code(md: Markdown) -> None:
210
+ """Assert future annotations do not leak into user code.
211
+
212
+ Parameters:
213
+ md: A Markdown instance (fixture).
214
+ """
215
+ html = md.convert(
216
+ dedent(
217
+ """
218
+ ```python exec="1"
219
+ class Int:
220
+ ...
221
+
222
+ def f(x: Int) -> None:
223
+ return x + 1.0
224
+
225
+ print(f"`{f.__annotations__['x']}`")
226
+ ```
227
+ """,
228
+ ),
229
+ )
230
+ assert "<code>Int</code>" not in html
231
+ assert re.search(r"class '_code_block_n\d+_\.Int'", html)
@@ -0,0 +1,87 @@
1
+ """Tests for the shell formatters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from textwrap import dedent
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ import pytest
10
+ from markdown import Markdown
11
+
12
+
13
+ def test_output_markdown(md: Markdown) -> None:
14
+ """Assert Markdown is converted to HTML.
15
+
16
+ Parameters:
17
+ md: A Markdown instance (fixture).
18
+ """
19
+ html = md.convert(
20
+ dedent(
21
+ """
22
+ ```sh exec="yes"
23
+ echo "**Bold!**"
24
+ ```
25
+ """,
26
+ ),
27
+ )
28
+ assert html == "<p><strong>Bold!</strong></p>"
29
+
30
+
31
+ def test_output_html(md: Markdown) -> None:
32
+ """Assert HTML is injected as is.
33
+
34
+ Parameters:
35
+ md: A Markdown instance (fixture).
36
+ """
37
+ html = md.convert(
38
+ dedent(
39
+ """
40
+ ```sh exec="yes" html="yes"
41
+ echo "**Bold!**"
42
+ ```
43
+ """,
44
+ ),
45
+ )
46
+ assert html == "<p>**Bold!**\n</p>"
47
+
48
+
49
+ def test_error_raised(md: Markdown, caplog: pytest.LogCaptureFixture) -> None:
50
+ """Assert errors properly log a warning and return a formatted traceback.
51
+
52
+ Parameters:
53
+ md: A Markdown instance (fixture).
54
+ caplog: Pytest fixture to capture logs.
55
+ """
56
+ html = md.convert(
57
+ dedent(
58
+ """
59
+ ```sh exec="yes"
60
+ echo("wrong syntax")
61
+ ```
62
+ """,
63
+ ),
64
+ )
65
+ assert "error" in html
66
+ assert "Execution of sh code block exited with unexpected code 2" in caplog.text
67
+
68
+
69
+ def test_return_code(md: Markdown, caplog: pytest.LogCaptureFixture) -> None:
70
+ """Assert return code is used correctly.
71
+
72
+ Parameters:
73
+ md: A Markdown instance (fixture).
74
+ caplog: Pytest fixture to capture logs.
75
+ """
76
+ html = md.convert(
77
+ dedent(
78
+ """
79
+ ```sh exec="yes" returncode="1"
80
+ echo Not in the mood
81
+ exit 1
82
+ ```
83
+ """,
84
+ ),
85
+ )
86
+ assert "Not in the mood" in html
87
+ assert "exited with" not in caplog.text
@@ -0,0 +1,89 @@
1
+ """Tests for the logic updating the table of contents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from textwrap import dedent
6
+ from typing import TYPE_CHECKING
7
+
8
+ from markdown.extensions.toc import TocExtension
9
+
10
+ if TYPE_CHECKING:
11
+ from markdown import Markdown
12
+
13
+
14
+ def test_updating_toc(md: Markdown) -> None:
15
+ """Assert ToC is updated with generated headings.
16
+
17
+ Parameters:
18
+ md: A Markdown instance (fixture).
19
+ """
20
+ TocExtension().extendMarkdown(md)
21
+ html = md.convert(
22
+ dedent(
23
+ """
24
+ ```python exec="yes"
25
+ print("# big heading")
26
+ ```
27
+ """,
28
+ ),
29
+ )
30
+ assert "<h1" in html
31
+ assert "big-heading" in md.toc # type: ignore[attr-defined]
32
+
33
+
34
+ def test_not_updating_toc(md: Markdown) -> None:
35
+ """Assert ToC is not updated with generated headings.
36
+
37
+ Parameters:
38
+ md: A Markdown instance (fixture).
39
+ """
40
+ TocExtension().extendMarkdown(md)
41
+ html = md.convert(
42
+ dedent(
43
+ """
44
+ ```python exec="yes" updatetoc="no"
45
+ print("# big heading")
46
+ ```
47
+ """,
48
+ ),
49
+ )
50
+ assert "<h1" in html
51
+ assert "big-heading" not in md.toc # type: ignore[attr-defined]
52
+
53
+
54
+ def test_both_updating_and_not_updating_toc(md: Markdown) -> None:
55
+ """Assert ToC is not updated with generated headings.
56
+
57
+ Parameters:
58
+ md: A Markdown instance (fixture).
59
+ """
60
+ TocExtension().extendMarkdown(md)
61
+ html = md.convert(
62
+ dedent(
63
+ """
64
+ ```python exec="yes" updatetoc="no"
65
+ print("# big heading")
66
+ ```
67
+
68
+ ```python exec="yes" updatetoc="yes"
69
+ print("## medium heading")
70
+ ```
71
+
72
+ ```python exec="yes" updatetoc="no"
73
+ print("### small heading")
74
+ ```
75
+
76
+ ```python exec="yes" updatetoc="yes"
77
+ print("#### tiny heading")
78
+ ```
79
+ """,
80
+ ),
81
+ )
82
+ assert "<h1" in html
83
+ assert "<h2" in html
84
+ assert "<h3" in html
85
+ assert "<h4" in html
86
+ assert "big-heading" not in md.toc # type: ignore[attr-defined]
87
+ assert "medium-heading" in md.toc # type: ignore[attr-defined]
88
+ assert "small-heading" not in md.toc # type: ignore[attr-defined]
89
+ assert "tiny-heading" in md.toc # type: ignore[attr-defined]
@@ -0,0 +1,25 @@
1
+ """Tests for the shell formatters."""
2
+
3
+ from textwrap import dedent
4
+
5
+ from markdown import Markdown
6
+
7
+
8
+ def test_output_markdown(md: Markdown) -> None:
9
+ """Assert we can highlight lines in the output.
10
+
11
+ Parameters:
12
+ md: A Markdown instance (fixture).
13
+ """
14
+ html = md.convert(
15
+ dedent(
16
+ """
17
+ ```tree hl_lines="2"
18
+ 1
19
+ 2
20
+ 3
21
+ ```
22
+ """,
23
+ ),
24
+ )
25
+ assert '<span class="hll">' in html
@@ -0,0 +1,38 @@
1
+ """Tests for the `validator` function."""
2
+
3
+ import pytest
4
+ from markdown.core import Markdown
5
+
6
+ from markdown_exec import validator
7
+
8
+
9
+ @pytest.mark.parametrize(
10
+ ("exec_value", "expected"),
11
+ [
12
+ ("yes", True),
13
+ ("YES", True),
14
+ ("on", True),
15
+ ("ON", True),
16
+ ("whynot", True),
17
+ ("true", True),
18
+ ("TRUE", True),
19
+ ("1", True),
20
+ ("-1", True),
21
+ ("0", False),
22
+ ("no", False),
23
+ ("NO", False),
24
+ ("off", False),
25
+ ("OFF", False),
26
+ ("false", False),
27
+ ("FALSE", False),
28
+ ],
29
+ )
30
+ def test_validate(md: Markdown, exec_value: str, expected: bool) -> None:
31
+ """Assert the validator returns True or False given inputs.
32
+
33
+ Parameters:
34
+ md: A Markdown instance.
35
+ exec_value: The exec option value, passed from the code block.
36
+ expected: Expected validation result.
37
+ """
38
+ assert validator("whatever", inputs={"exec": exec_value}, options={}, attrs={}, md=md) is expected
File without changes
File without changes