this removes Flask, reworks a number of library methods accordingly, and adds generators and build commands to process the instance directory (largely unchanged, except config.py is now config.json) and spit out files suitable to be served by a web server such as Nginx. there are probably some rough edges here, but overall this works. also note, as this is no longer server software on a network, the license has changed from AGPLv3 to GPLv3, and the "or any later version" allowance has been removed Signed-off-by: Brian S. Stephan <bss@incorporeal.org>
59 lines
2.0 KiB
Python
59 lines
2.0 KiB
Python
"""Serve dot diagrams inline.
|
|
|
|
SPDX-FileCopyrightText: © 2021 Brian S. Stephan <bss@incorporeal.org>
|
|
SPDX-License-Identifier: GPL-3.0-only
|
|
"""
|
|
import base64
|
|
import logging
|
|
import re
|
|
|
|
import markdown
|
|
import pydot
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class InlinePydot(markdown.Extension):
|
|
"""Wrap the markdown prepcoressor."""
|
|
|
|
def extendMarkdown(self, md):
|
|
"""Add InlinePydotPreprocessor to the Markdown instance."""
|
|
md.preprocessors.register(InlinePydotPreprocessor(md), 'dot_block', 100)
|
|
|
|
|
|
class InlinePydotPreprocessor(markdown.preprocessors.Preprocessor):
|
|
"""Identify dot codeblocks and run them through pydot."""
|
|
|
|
BLOCK_RE = re.compile(r'~~~{\s+pydot:(?P<filename>[^\s]+)\s+}\n(?P<content>.*?)~~~', re.DOTALL)
|
|
|
|
def run(self, lines):
|
|
"""Match and generate diagrams from dot code blocks."""
|
|
text = '\n'.join(lines)
|
|
out = text
|
|
for block_match in self.BLOCK_RE.finditer(text):
|
|
filename = block_match.group(1)
|
|
dot_string = block_match.group(2)
|
|
logger.debug("matched markdown block: %s", dot_string)
|
|
logger.debug("match start/end: %s/%s", block_match.start(), block_match.end())
|
|
|
|
# use pydot to turn the text into pydot
|
|
graphs = pydot.graph_from_dot_data(dot_string)
|
|
if not graphs:
|
|
logger.debug("some kind of issue with parsed 'dot' %s", dot_string)
|
|
raise ValueError("error parsing dot text!")
|
|
|
|
# encode the image and provide as an inline image in markdown
|
|
encoded_image = base64.b64encode(graphs[0].create_png()).decode('ascii')
|
|
data_path = f'data:image/png;base64,{encoded_image}'
|
|
inline_image = f''
|
|
|
|
# replace the image in the output markdown
|
|
out = out.replace(block_match.group(0), inline_image)
|
|
|
|
return out.split('\n')
|
|
|
|
|
|
def makeExtension(*args, **kwargs):
|
|
"""Provide the extension to the markdown extension loader."""
|
|
return InlinePydot(*args, **kwargs)
|