|
1 | 1 | """Formatter and utils for executing Python code.""" |
2 | 2 |
|
3 | 3 | import traceback |
4 | | -from copy import deepcopy |
| 4 | +from textwrap import indent |
5 | 5 |
|
6 | 6 | from markdown.core import Markdown |
7 | 7 | from markupsafe import Markup |
8 | 8 |
|
| 9 | +from markdown_exec.utils import code_block, tabbed |
| 10 | + |
| 11 | +md_copy = None |
| 12 | + |
9 | 13 |
|
10 | 14 | class MarkdownOutput(Exception): # noqa: N818 |
11 | 15 | """Exception to return Markdown.""" |
@@ -39,22 +43,42 @@ def output_html(text: str) -> None: |
39 | 43 | raise HTMLOutput(text) |
40 | 44 |
|
41 | 45 |
|
42 | | -def exec_python(source: str, md: Markdown) -> str: |
| 46 | +def exec_python(source: str, md: Markdown, isolate: bool = False, show_source: str = "", **options) -> str: |
43 | 47 | """Execute code and return HTML. |
44 | 48 |
|
45 | 49 | Parameters: |
46 | 50 | source: The code to execute. |
47 | 51 | md: The Markdown instance. |
| 52 | + isolate: Whether to run the code in isolation. |
| 53 | + show_source: Whether to show source as well, and where. |
48 | 54 |
|
49 | 55 | Returns: |
50 | 56 | HTML contents. |
51 | 57 | """ |
| 58 | + global md_copy |
| 59 | + if md_copy is None: |
| 60 | + md_copy = Markdown() |
| 61 | + md_copy.registerExtensions(md.registeredExtensions, {}) |
| 62 | + if isolate: |
| 63 | + exec_source = f"def _function():\n{indent(source, prefix=' ' * 4)}\n_function()\n" |
| 64 | + else: |
| 65 | + exec_source = source |
52 | 66 | try: |
53 | | - exec(source) # noqa: S102 |
54 | | - except MarkdownOutput as output: |
55 | | - return Markup(deepcopy(md).convert(str(output))) |
56 | | - except HTMLOutput as output: |
57 | | - return str(output) |
| 67 | + exec(exec_source) # noqa: S102 |
| 68 | + except MarkdownOutput as raised_output: |
| 69 | + output = str(raised_output) |
| 70 | + except HTMLOutput as raised_output: |
| 71 | + output = f'<div markdown="0">{str(raised_output)}</div>' |
58 | 72 | except Exception: |
59 | | - return Markup(deepcopy(md).convert(f"```python\n{traceback.format_exc()}\n```")) |
60 | | - return "" |
| 73 | + output = code_block("python", traceback.format_exc()) |
| 74 | + if show_source: |
| 75 | + source_block = code_block("python", source) |
| 76 | + if show_source == "above": |
| 77 | + output = source_block + "\n\n" + output |
| 78 | + elif show_source == "below": |
| 79 | + output = output + "\n\n" + source_block |
| 80 | + elif show_source == "tabbed-left": |
| 81 | + output = tabbed(("Source", source_block), ("Result", output)) |
| 82 | + elif show_source == "tabbed-right": |
| 83 | + output = tabbed(("Result", output), ("Source", source_block)) |
| 84 | + return Markup(md_copy.convert(output)) |
0 commit comments