Use markdown-code-runner instead of packaged solution (#505)

* Use markdown-code-runner instead of packaged solution

* add debug
This commit is contained in:
Bas Nijholt 2023-04-02 18:56:36 -07:00 committed by GitHub
commit 8108bd0a4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 5 additions and 201 deletions

View file

@ -1,195 +0,0 @@
# Copyright (c) 2023, Bas Nijholt
# All rights reserved.
# When using this code, please cite the original source.
# and include the LICENSE file in your project.
"""Automatically update Markdown files with code block output.
Add code blocks between <!-- START_CODE --> and <!-- END_CODE --> in your Markdown file.
The output will be inserted between <!-- START_OUTPUT --> and <!-- END_OUTPUT -->.
Example:
-------
```
<!-- START_CODE -->
<!-- print('Hello, world!') -->
<!-- END_CODE -->
<!-- START_OUTPUT -->
This will be replaced by the output of the code block above.
<!-- END_OUTPUT -->
```
"""
from __future__ import annotations
import contextlib
import io
from pathlib import Path
def md_comment(text: str) -> str:
"""Format a string as a Markdown comment."""
return f"<!-- {text} -->"
MARKERS = {
"warning": md_comment("THIS CONTENT IS AUTOMATICALLY GENERATED"),
"start_code": md_comment("START_CODE"),
"end_code": md_comment("END_CODE"),
"start_output": md_comment("START_OUTPUT"),
"end_output": md_comment("END_OUTPUT"),
}
def remove_md_comment(commented_text: str) -> str:
"""Remove Markdown comment tags from a string."""
if not (commented_text.startswith("<!-- ") and commented_text.endswith(" -->")):
raise ValueError("Invalid Markdown comment format")
return commented_text[5:-4]
def execute_code_block(code: list[str]) -> list[str]:
"""Execute a code block and return its output as a list of strings."""
f = io.StringIO()
with contextlib.redirect_stdout(f):
exec("\n".join(code)) # noqa: S102
return f.getvalue().split("\n")
def process_markdown(content: list[str]) -> list[str]:
"""Executes code blocks in a list of Markdown-formatted strings and returns the modified list.
Parameters
----------
content
A list of Markdown-formatted strings.
Returns
-------
list[str]
A modified list of Markdown-formatted strings with code block output inserted.
"""
assert isinstance(content, list), "Input must be a list"
new_lines = []
code = []
in_code_block = in_output_block = False
output = None
for line in content:
if MARKERS["start_code"] in line:
in_code_block = True
elif MARKERS["start_output"] in line:
in_output_block = True
new_lines.extend([line, MARKERS["warning"]] + output)
output = None
elif MARKERS["end_output"] in line:
in_output_block = False
elif in_code_block:
if MARKERS["end_code"] in line:
in_code_block = False
output = execute_code_block(code)
code = []
else:
code.append(remove_md_comment(line))
if not in_output_block:
new_lines.append(line)
return new_lines
def update_markdown_file(filepath: Path) -> None:
"""Rewrite a Markdown file by executing and updating code blocks."""
with filepath.open() as f:
original_lines = [line.rstrip("\n") for line in f.readlines()]
new_lines = process_markdown(original_lines)
updated_content = "\n".join(new_lines).rstrip() + "\n"
with filepath.open("w") as f:
f.write(updated_content)
def test_process_markdown():
def assert_process(input_lines, expected_output):
output = process_markdown(input_lines)
assert output == expected_output, f"Expected {expected_output}, got {output}"
# Test case 1: Single code block
input_lines = [
"Some text",
MARKERS["start_code"],
md_comment("print('Hello, world!')"),
MARKERS["end_code"],
MARKERS["start_output"],
"This content will be replaced",
MARKERS["end_output"],
"More text",
]
expected_output = [
"Some text",
MARKERS["start_code"],
md_comment("print('Hello, world!')"),
MARKERS["end_code"],
MARKERS["start_output"],
MARKERS["warning"],
"Hello, world!",
"",
MARKERS["end_output"],
"More text",
]
assert_process(input_lines, expected_output)
# Test case 2: Two code blocks
input_lines = [
"Some text",
MARKERS["start_code"],
md_comment("print('Hello, world!')"),
MARKERS["end_code"],
MARKERS["start_output"],
"This content will be replaced",
MARKERS["end_output"],
"More text",
MARKERS["start_code"],
md_comment("print('Hello again!')"),
MARKERS["end_code"],
MARKERS["start_output"],
"This content will also be replaced",
MARKERS["end_output"],
]
expected_output = [
"Some text",
MARKERS["start_code"],
md_comment("print('Hello, world!')"),
MARKERS["end_code"],
MARKERS["start_output"],
MARKERS["warning"],
"Hello, world!",
"",
MARKERS["end_output"],
"More text",
MARKERS["start_code"],
md_comment("print('Hello again!')"),
MARKERS["end_code"],
MARKERS["start_output"],
MARKERS["warning"],
"Hello again!",
"",
MARKERS["end_output"],
]
assert_process(input_lines, expected_output)
# Test case 3: No code blocks
input_lines = [
"Some text",
"More text",
]
expected_output = [
"Some text",
"More text",
]
assert_process(input_lines, expected_output)
if __name__ == "__main__":
test_process_markdown()
update_markdown_file(Path(__file__).parent.parent / "README.md")

View file

@ -5,10 +5,9 @@ on:
branches:
- master
paths:
- ".github/update-readme.py"
- "README.md"
- ".github/workflows/update-readme.yml"
- "custom_components/adaptive_lighting/const.py"
- "github/workflows/update-readme.yml"
pull_request:
jobs:
@ -25,10 +24,10 @@ jobs:
- name: Install pandas and tabulate
run: |
pip install pandas tabulate
pip install markdown-code-runner pandas tabulate
- name: Run update-readme.py
run: python ./.github/update-readme.py
- name: Run markdown-code-runner
run: markdown-code-runner --debug README.md
- name: Commit updated README.md
run: |

View file

@ -82,7 +82,7 @@ The YAML and frontend configuration methods support all of the options listed be
<!-- START_CODE -->
<!-- import os, sys -->
<!-- sys.path.append(os.path.join(os.path.dirname(__file__), '..')) -->
<!-- sys.path.append(os.path.abspath(".")) -->
<!-- from custom_components.adaptive_lighting import const -->
<!-- markdown_table = const.generate_markdown_table() -->
<!-- print(markdown_table) -->