Skip to content

Commit

Permalink
feat: Add support for shell code blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
pawamoy committed May 9, 2022
1 parent de8309e commit f2b4b67
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 39 deletions.
19 changes: 16 additions & 3 deletions docs/gallery.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
---
hide:
- navigation
---

# Gallery

Welcome to our gallery of examples!

## Diagrams (cloud/system architecture)

[Diagrams](https:/mingrammer/diagrams) offers a nice way of building
Expand Down Expand Up @@ -61,10 +70,14 @@ svg = svg.replace('fill="white"', 'fill="transparent"')
reference = "../reference"
modules = (
"markdown_exec",
"markdown_exec.pycon",
"markdown_exec.python",
"markdown_exec.rendering",
"markdown_exec.formatters.base",
"markdown_exec.formatters.bash",
"markdown_exec.formatters.mardown",
"markdown_exec.formatters.pycon",
"markdown_exec.formatters.python",
"markdown_exec.formatters.sh",
"markdown_exec.mkdocs_plugin",
"markdown_exec.rendering",
)
for module in modules:
svg_title = module.replace(".", "_")
Expand Down
29 changes: 4 additions & 25 deletions docs/usage.md → docs/usage/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Usage

## HTML vs. Markdown

By default, Markdown Exec will render what you print as Markdown.
Expand Down Expand Up @@ -87,16 +89,15 @@ with one of the following values:

---

**Console** <small>(best used with actual session syntax like `pycon`,
details at [Python console code](#python-console-code))</small>:
**Console** <small>(best used with actual session syntax like
[`pycon`](python/#python-console-code) or [`console`](shell/#console))</small>:

````md exec="1" source="tabbed-left" tabs="Markdown|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 @@ -150,28 +151,6 @@ Example:
```
````

## 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.

````md exec="1" source="tabbed-left" tabs="Markdown|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:

````md exec="1" source="tabbed-left" tabs="Markdown|Rendered"
```pycon exec="1" source="console"
--8<-- "multiple.pycon"
```
````

## Literate Markdown

With this extension, it is also possible to write "literate programming" Markdown.
Expand Down
23 changes: 23 additions & 0 deletions docs/usage/python.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Python

## 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.

````md exec="1" source="tabbed-left" tabs="Markdown|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:

````md exec="1" source="tabbed-left" tabs="Markdown|Rendered"
```pycon exec="1" source="console"
--8<-- "multiple.pycon"
```
````
21 changes: 21 additions & 0 deletions docs/usage/shell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Shell

Shell code blocks are executed using the same interpreter specified
as language of the code block, in sub-processes. The output is captured
and rendered as Markdown or HTML (see [Usage](../#html-vs-markdown)).

## Bash

````md exec="1" source="tabbed-left" tabs="Markdown|Rendered"
```sh exec="1" source="material-block"
pdm --version
```
````

## Console

````md exec="1" source="tabbed-left" tabs="Markdown|Rendered"
```console exec="1" source="console"
$ pdm --version
```
````
6 changes: 5 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ nav:
- Changelog: changelog.md
- Credits: credits.md
- License: license.md
- Usage: usage.md
- Usage:
- usage/index.md
- Python: usage/python.md
- Shell: usage/shell.md
- Gallery: gallery.md
# defer to gen-files + literate-nav
- Code Reference: reference/
Expand All @@ -27,6 +30,7 @@ theme:
logo: material/currency-sign
features:
- navigation.tabs
- navigation.tabs.sticky
- navigation.top
palette:
- media: "(prefers-color-scheme: light)"
Expand Down
26 changes: 16 additions & 10 deletions src/markdown_exec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,25 @@

from markdown import Markdown

from markdown_exec.formatters.markdown import format_markdown
from markdown_exec.formatters.pycon import format_pycon
from markdown_exec.formatters.python import format_python
from markdown_exec.formatters.bash import _format_bash # noqa: WPS450
from markdown_exec.formatters.console import _format_console # noqa: WPS450
from markdown_exec.formatters.markdown import _format_markdown # noqa: WPS450
from markdown_exec.formatters.pycon import _format_pycon # noqa: WPS450
from markdown_exec.formatters.python import _format_python # noqa: WPS450
from markdown_exec.formatters.sh import _format_sh # noqa: WPS450

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


_formatters = {
"md": format_markdown,
"markdown": format_markdown,
"py": format_python,
"python": format_python,
"pycon": format_pycon,
formatters = {
"bash": _format_bash,
"console": _format_console,
"md": _format_markdown,
"markdown": _format_markdown,
"py": _format_python,
"python": _format_python,
"pycon": _format_pycon,
"sh": _format_sh,
}

# negative look behind: matches only if | (pipe) if not preceded by \ (backslash)
Expand Down Expand Up @@ -91,7 +97,7 @@ def formatter(
Returns:
HTML contents.
"""
fmt = _formatters.get(language, lambda source, *args, **kwargs: source)
fmt = formatters.get(language, lambda source, *args, **kwargs: source)
return fmt(source, md, **options)


Expand Down
20 changes: 20 additions & 0 deletions src/markdown_exec/formatters/bash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Formatter for executing shell code."""

from __future__ import annotations

import subprocess # noqa: S404

from markdown_exec.formatters.base import base_format
from markdown_exec.rendering import code_block


def _run_bash(code: str, **extra: str) -> str:
try:
output = subprocess.check_output(["bash", "-c", code]).decode() # noqa: S603,S607
except subprocess.CalledProcessError as error:
return code_block("bash", error.output, **extra)
return output


def _format_bash(*args, **kwargs) -> str:
return base_format("bash", _run_bash, *args, **kwargs)
41 changes: 41 additions & 0 deletions src/markdown_exec/formatters/console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Formatter for executing shell console code."""

from __future__ import annotations

import textwrap
from typing import Any
from uuid import uuid4

from markdown.core import Markdown

from markdown_exec.formatters.sh import _run_sh # noqa: WPS450
from markdown_exec.rendering import add_source, markdown


def _format_console( # noqa: WPS231
code: str,
md: Markdown,
html: bool,
source: str,
tabs: tuple[str, str],
**options: Any,
) -> str:
markdown.setup(md)

sh_lines = []
for line in code.split("\n"):
if line.startswith("$ "):
sh_lines.append(line[2:])
sh_code = "\n".join(sh_lines)

extra = options.get("extra", {})
output = _run_sh(sh_code, **extra)
stash = {}
if html:
placeholder = str(uuid4())
stash[placeholder] = output
output = placeholder
if source:
source_code = textwrap.indent(sh_code, "$ ")
output = add_source(source=source_code, location=source, output=output, language="console", tabs=tabs, **extra)
return markdown.convert(output, stash=stash)
20 changes: 20 additions & 0 deletions src/markdown_exec/formatters/sh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Formatter for executing shell code."""

from __future__ import annotations

import subprocess # noqa: S404

from markdown_exec.formatters.base import base_format
from markdown_exec.rendering import code_block


def _run_sh(code: str, **extra: str) -> str:
try:
output = subprocess.check_output(["sh", "-c", code]).decode() # noqa: S603,S607
except subprocess.CalledProcessError as error:
return code_block("sh", error.output, **extra)
return output


def _format_sh(*args, **kwargs) -> str:
return base_format("sh", _run_sh, *args, **kwargs)

0 comments on commit f2b4b67

Please sign in to comment.