Source code for ycleptic.src.makedoc

# Author: Cameron F. Abrams <cfa22@drexel.edu>

"""
The ``make-doc`` subcommand implementation
"""
from __future__ import annotations
import io
import logging
import shutil
from pathlib import Path
from .. import __version__
from .stringthings import my_indent, dict_to_rst_yaml_block, generate_footer

logger = logging.getLogger(__name__)

[docs] def make_doc(L: list[dict], topname: str, toptext: str, fp: io.TextIOWrapper, docname: str = '', doctext: str = '', docexample: dict = {}, rootdir: str = '', footer_style: str = 'paragraph'): """ Makes a sphinx/rtd-style doctree from the base config file provided including a root node. This is a recursive function that will create a directory structure based on the attributes and subattributes in the provided list `L`. It will create a main file with the name `topname` and write the documentation for the top-level attributes, as well as any subattributes or single-valued attributes. Parameters ---------- L : list of dict List of attributes and subattributes to document. topname : str The bare stem name of the current RST file (e.g. ``config_ref`` or ``attribute_1``). Used only for toctree entry generation; the actual output path is derived from ``fp``. toptext : str The text to include at the top of the documentation file. fp : io.TextIOWrapper Open file handle to write the documentation into. docname : str Title string for the document. Optional; defaults to the topname in double backticks. doctext : str Introductory text for the document. Optional; defaults to ``toptext``. docexample : dict Example to render as a YAML code block. Optional. rootdir : str Absolute path to the Sphinx source directory. Used to compute RST cross-reference labels relative to the source root. Optional. footer_style : str Style of the auto-generated footer. Optional; defaults to ``"paragraph"``. """ # Derive absolute paths from the open file handle so this function works # regardless of the process working directory. fp_path = Path(fp.name).resolve() rootpath = Path(rootdir).resolve() if rootdir else fp_path.parent rel = fp_path.with_suffix('').relative_to(rootpath) # e.g. PosixPath('config_ref/attribute_1') outdir = fp_path.parent / fp_path.stem # where sub-RST files are written if docname == '': docname = f'``{topname}``' if doctext == '': doctext = toptext logger.debug(f'"{rel}"') fp.write(f'.. _{" ".join(rel.parts)}:\n\n') fp.write(f'{docname}\n{"="*(len(docname))}\n\n') if doctext: fp.write(f'{doctext}\n\n') if docexample: fp.write('Example:\n' + '+'*len('Example:') + '\n\n') fp.write(f'{dict_to_rst_yaml_block(docexample)}\n\n') svp = [d for d in L if 'attributes' not in d] svp_w_contdef = [d for d in svp if isinstance(d.get('default', None), (dict, list))] svp_simple = [d for d in svp if not isinstance(d.get('default', None), (dict, list))] sd = [d for d in L if 'attributes' in d] if any(isinstance(sv.get('default', None), (dict, list)) for sv in svp) or len(sd) > 0: if outdir.exists(): shutil.rmtree(outdir) outdir.mkdir() if len(svp_simple) > 0: ess = 's' if len(svp_simple) > 1 else '' fp.write(f'Single-valued attribute{ess}:\n\n') for sv in svp_simple: default = sv.get('default', None) default_text = f' (default: {default})' if default is not None else '' fp.write(f' * ``{sv["name"]}``: {sv["text"]}{default_text}\n\n') sv_example = sv.get('docs', {}).get('example', {}) if sv_example: fp.write(' Example:\n\n') fp.write(f'{my_indent(dict_to_rst_yaml_block(sv_example), indent=4)}\n\n') fp.write('\n\n') if len(svp_w_contdef) > 0: ess = 's' if len(svp_w_contdef) > 1 else '' fp.write(f'Container-like attribute{ess}:\n\n') fp.write('.. toctree::\n :maxdepth: 1\n\n') for s in svp_w_contdef: fp.write(f' {topname}/{s["name"]}\n') fp.write('\n\n') if len(sd) > 0: ess = 's' if len(sd) > 1 else '' fp.write(f'Subattribute{ess}:\n\n') fp.write('.. toctree::\n :maxdepth: 1\n\n') for s in sd: fp.write(f' {topname}/{s["name"]}\n') fp.write('\n\n') fp.write(generate_footer(app_name=__package__.split('.')[0], version=__version__, style=footer_style)) fp.close() if len(svp_w_contdef) > 0: for s in svp_w_contdef: name = s["name"] default = s["default"] text = s.get('text', '') sub_doctext = s.get('docs', {}).get('text', text) sub_example = s.get('docs', {}).get('example', {}) sub_rel = rel / name with open(outdir / f'{name}.rst', 'w') as f: f.write(f'.. _{" ".join(sub_rel.parts)}:\n\n') f.write(f'``{name}``\n{"-"*(4+len(name))}\n\n') if isinstance(default, list): for d in default: f.write(f' * {d}\n') elif isinstance(default, dict): for k, v in default.items(): f.write(f' * ``{k}``: {v}\n') f.write('\n\n') if sub_doctext: f.write(f'{sub_doctext}\n\n') if sub_example: f.write('Example:\n' + '+'*len('Example:') + '\n\n') f.write(f'{dict_to_rst_yaml_block(sub_example)}\n\n') f.write(generate_footer(app_name=__package__.split('.')[0], version=__version__, style=footer_style)) if len(sd) > 0: for s in sd: name = s["name"] doc = s.get('docs', {}) with open(outdir / f'{name}.rst', 'w') as f: make_doc(s['attributes'], name, s['text'], f, docname=doc.get('title', ''), doctext=doc.get('text', ''), docexample=doc.get('example', {}), rootdir=str(rootpath), footer_style=footer_style)