Skip to content

Commit

Permalink
feat: Support pycon code blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
pawamoy committed May 1, 2022
1 parent fc56702 commit 2c86394
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 2 deletions.
6 changes: 6 additions & 0 deletions docs/snippets/multiple.pycon
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
>>> name = "Baron"
>>> print(name)
Baron
>>> age = "???"
>>> print(age)
???
2 changes: 2 additions & 0 deletions docs/snippets/source.pycon
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
>>> print("I'm the result!")
I'm not the result...
61 changes: 60 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ with one of the following values:
- `below`: The source code will be rendered below the result.
- `tabbed-left`: The source code and result will be rendered in tabs, in that order (remember to enable the `pymdownx.tabbed` extension).
- `tabbed-right`: The result and source code will be rendered in tabs, in that order (remember to enable the `pymdownx.tabbed` extension).
- `console`: The source and result are concatenated in a single code block, like an interactive console session.

Source above:

Expand Down Expand Up @@ -119,6 +120,24 @@ Tabbed on the right:
--8<-- "source.py"
```

Console (best used with actual session syntax like `pycon`,
details at [Python console code](#python-console-code)):

=== "Markdown"

````md
```pycon exec="true" source="console"
--8<-- "source.pycon"
```
````

=== "Rendered"

```pycon exec="true" source="console"
--8<-- "source.pycon"
```


## Change the titles of tabs

In the previous example, we didn't specify any title for tabs,
Expand Down Expand Up @@ -194,4 +213,44 @@ Example:

```python exec="1" source="above" title="source.py"
--8<-- "source.py"
```
```

## Python console code

Code blocks syntax-highlighted with the `pycon` identifier are also supported.
These code blocks will be pre-processed to keep only the lines
starting with `>>> `, and the chevrons (prompt) will be removed from these lines,
so we can execute them.

=== "Markdown"

````md
```pycon exec="1" source="console"
--8<-- "source.pycon"
```
````

=== "Rendered"

```pycon exec="1" source="console"
--8<-- "source.pycon"
```


It also means that multiple blocks of instructions will be concatenated,
as well as their output:


=== "Markdown"

````md
```pycon exec="1" source="console"
--8<-- "multiple.pycon"
```
````

=== "Rendered"

```pycon exec="1" source="console"
--8<-- "multiple.pycon"
```
2 changes: 2 additions & 0 deletions src/markdown_exec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@

from markdown import Markdown

from markdown_exec.pycon import format_pycon
from markdown_exec.python import format_python

__all__: list[str] = ["formatter", "validator"] # noqa: WPS410


_formatters = {
"python": format_python,
"pycon": format_pycon,
}

# negative look behind: matches only if | (pipe) if not preceded by \ (backslash)
Expand Down
2 changes: 1 addition & 1 deletion src/markdown_exec/mkdocs_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
class MarkdownExecPlugin(BasePlugin):
"""MkDocs plugin to easily enable custom fences for code blocks execution."""

config_scheme = (("languages", config_options.Type(list, default=["python"])),)
config_scheme = (("languages", config_options.Type(list, default=["python", "pycon"])),)

def on_config(self, config: Config, **kwargs) -> Config: # noqa: D102
self.languages = self.config["languages"]
Expand Down
48 changes: 48 additions & 0 deletions src/markdown_exec/pycon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Formatter for executing `pycon` code."""

from __future__ import annotations

import textwrap
from typing import Any

from markdown.core import Markdown

from markdown_exec.python import run_python
from markdown_exec.rendering import add_source, markdown


def format_pycon( # noqa: WPS231
code: str,
md: Markdown,
html: bool,
source: str,
tabs: tuple[str, str],
**options: Any,
) -> str:
"""Execute `pycon` code and return HTML.
Parameters:
code: The code to execute.
md: The Markdown instance.
html: Whether to inject output as HTML directly, without rendering.
source: Whether to show source as well, and where.
tabs: Titles of tabs (if used).
**options: Additional options passed from the formatter.
Returns:
HTML contents.
"""
markdown.mimic(md)

python_lines = []
for line in code.split("\n"):
if line.startswith(">>> "):
python_lines.append(line[4:])
python_code = "\n".join(python_lines)

extra = options.get("extra", {})
output = run_python(python_code, html, **extra)
if source:
source_code = textwrap.indent(python_code, ">>> ")
output = add_source(source=source_code, location=source, output=output, language="pycon", tabs=tabs, **extra)
return markdown.convert(output)

0 comments on commit 2c86394

Please sign in to comment.