Export documents to other formats.ΒΆ

Literate programs can be translated into many formats. Commonly markup langauges focus on translation to other markup languages, we add an extra ability to convert markup to source code.

import pidgy, pathlib, typing, textwrap, IPython
try: from . import util
except: import util

We can reuse existing nbconvert machinery if we expand every file to a notebook.

def file_to_nb(file: pathlib.Path) -> "nbformat.NotebookNode":
    import nbformat
    if file.suffix in {'.md', '.markdown'}:
        return nbformat.v4.new_notebook(cells=[nbformat.v4.new_code_cell(file.read_text())])
    return nbformat.reads(file.read_text(), 4)

A notebook can also be flattened.

def flattennb(nb: typing.Union[str, "nbformat.NotebookNode"]):
    if isinstance(nb, str): return nb
    return [textwrap.indent(''.join(x.source), x.cell_type != 'code' and '# ' or '') for x in nb.cells]

def to_markup(input: typing.Union[str, "nbformat.NotebookNode"], exporter: "nbconvert.Exporter") -> str:
    return exporter.from_notebook_node(input)[0]

def to_python(input, tangle = pidgy.tangle.pidgyManager()):
    import black, isort
    code = pidgy.loader.tangle(flattennb(input))
    code = isort.SortImports(file_contents=code).output
    code = black.format_str(code, mode=black.FileMode(line_length=100))
    return code

def convert(*files, to: {'python', 'markdown'}, write: bool=False):
    import nbconvert
    exporter = nbconvert.get_exporter(to)()
    for file in util.yield_files(files):
        nb = file_to_nb(file)
        if to =='python':
            body = '\n'.join(to_python(''.join(getattr(x, 'source', []))) for x in nb.cells)
        else:
            body = to_markup(nb, exporter)
        if write:
            new = pathlib.Path(file).with_suffix(dict(python='.py', markdown='.md')[to])
            new.write_text(body)
            __import__('click').echo(F"{new} created.")
        else:
            __import__('click').echo(body)

https://stackoverflow.com/questions/34643620/how-can-i-split-my-click-commands-each-with-a-set-of-sub-commands-into-multipl