Skip to content

Commit

Permalink
refactor: Add reusable base formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
pawamoy committed May 9, 2022
1 parent e6b67cd commit c265bee
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 111 deletions.
Empty file removed docs/snippets/literate_markdown.md
Empty file.
49 changes: 49 additions & 0 deletions src/markdown_exec/formatters/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Generic formatter for executing code."""

from __future__ import annotations

from typing import Any, Callable
from uuid import uuid4

from markdown.core import Markdown
from markupsafe import Markup

from markdown_exec.rendering import add_source, markdown


def base_format( # noqa: WPS231
language: str,
run: Callable,
code: str,
md: Markdown,
html: bool,
source: str,
tabs: tuple[str, str],
**options: Any,
) -> Markup:
"""Execute code and return HTML.
Parameters:
language: The code language.
run: Function that runs code and returns output.
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.setup(md)
extra = options.get("extra", {})
output = run(code, **extra)
stash = {}
if html:
placeholder = str(uuid4())
stash[placeholder] = output
output = placeholder
if source:
output = add_source(source=code, location=source, output=output, language=language, tabs=tabs, **extra)
return markdown.convert(output, stash=stash)
43 changes: 4 additions & 39 deletions src/markdown_exec/formatters/markdown.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,9 @@
"""Formatter for reapply literate Markdown."""
"""Formatter for literate Markdown."""

from __future__ import annotations

from typing import Any
from uuid import uuid4
from markdown_exec.formatters.base import base_format

from markdown.core import Markdown

from markdown_exec.rendering import add_source, markdown


def format_markdown( # noqa: WPS231
code: str,
md: Markdown,
html: bool,
source: str,
tabs: tuple[str, str],
**options: Any,
) -> str:
"""Reapply Markdown 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.setup(md)
extra = options.get("extra", {})
output = code
stash = {}
if html:
placeholder = str(uuid4())
stash[placeholder] = output
output = placeholder
if source:
output = add_source(source=code, location=source, output=output, language="md", tabs=tabs, **extra)
return markdown.convert(output, stash=stash)
def _format_markdown(*args, **kwargs) -> str:
return base_format("md", lambda code, **_: code, *args, **kwargs)
19 changes: 3 additions & 16 deletions src/markdown_exec/formatters/pycon.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,18 @@

from markdown.core import Markdown

from markdown_exec.formatters.python import run_python
from markdown_exec.formatters.python import _run_python # noqa: WPS450
from markdown_exec.rendering import add_source, markdown


def format_pycon( # noqa: WPS231
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.setup(md)

python_lines = []
Expand All @@ -42,7 +29,7 @@ def format_pycon( # noqa: WPS231
python_code = "\n".join(python_lines)

extra = options.get("extra", {})
output = run_python(python_code, **extra)
output = _run_python(python_code, **extra)
stash = {}
if html:
placeholder = str(uuid4())
Expand Down
63 changes: 7 additions & 56 deletions src/markdown_exec/formatters/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,18 @@
from functools import partial
from io import StringIO
from typing import Any
from uuid import uuid4

from markdown.core import Markdown
from markdown_exec.formatters.base import base_format
from markdown_exec.rendering import code_block

from markdown_exec.rendering import add_source, code_block, markdown


def buffer_print(buffer: StringIO, *text: str, end: str = "\n", **kwargs: Any) -> None:
"""Print Markdown.
Parameters:
buffer: A string buffer to write into.
*text: The text to write into the buffer. Multiple strings accepted.
end: The string to write at the end.
**kwargs: Other keyword arguments passed to `print` are ignored.
"""
def _buffer_print(buffer: StringIO, *text: str, end: str = "\n", **kwargs: Any) -> None:
buffer.write(" ".join(text) + end)


def run_python(code: str, **extra: str) -> str:
"""Run Python code using `exec` and return its output.
Parameters:
code: The code to execute.
**extra: Extra options passed to the traceback code block in case of errors.
Returns:
The output.
"""
def _run_python(code: str, **extra: str) -> str:
buffer = StringIO()
exec_globals = {"print": partial(buffer_print, buffer)}
exec_globals = {"print": partial(_buffer_print, buffer)}

try:
exec(code, {}, exec_globals) # noqa: S102
Expand All @@ -50,35 +31,5 @@ def run_python(code: str, **extra: str) -> str:
return buffer.getvalue()


def format_python( # noqa: WPS231
code: str,
md: Markdown,
html: bool,
source: str,
tabs: tuple[str, str],
**options: Any,
) -> str:
"""Execute `python` 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.setup(md)
extra = options.get("extra", {})
output = run_python(code, **extra)
stash = {}
if html:
placeholder = str(uuid4())
stash[placeholder] = output
output = placeholder
if source:
output = add_source(source=code, location=source, output=output, language="python", tabs=tabs, **extra)
return markdown.convert(output, stash=stash)
def _format_python(*args, **kwargs) -> str:
return base_format("python", _run_python, *args, **kwargs)

0 comments on commit c265bee

Please sign in to comment.