CoolFace
Apppublic

braindecode/model-explorer

sourceHugging Facebsd-3-clauseupdated 5mo agoView on Hugging Face
0likes
docstring_renderer.py172 linesDownload Raw Back to root
1"""Render braindecode rST docstrings to HTML for the model-explorer Space.2 3Braindecode docstrings use NumpyDoc + Sphinx extensions:4  .. figure::          (architecture image)5  :bdg-danger:         (sphinx-design colored badges)6  .. versionadded::    (Sphinx admonition)7  .. important::       (admonition)8  .. code-block::      (highlighted code)9  [ref]_               (NumpyDoc citation reference)10 11Pure docutils does not know about the Sphinx directives. Rather than12spinning up a full Sphinx build inside the Space, we:13 141. Pre-process the docstring with regex substitutions that map15   Sphinx-only directives to plain rST equivalents docutils can parse.162. Hand the result to docutils.publish_parts for rST -> HTML.173. Wrap with a small CSS style block that matches braindecode.org colors.18 19This keeps the Space dependency-light (no sphinx at runtime) while still20rendering figures, headings, parameter tables, and references.21"""22 23from __future__ import annotations24 25import inspect26import re27from textwrap import dedent28 29from docutils.core import publish_parts30from docutils.utils import SystemMessage31 32_BADGE_COLORS = {33    "bdg-danger": "#d9534f",34    "bdg-success": "#5cb85c",35    "bdg-primary": "#0072B2",36    "bdg-info": "#56B4E9",37    "bdg-warning": "#E69F00",38    "bdg-secondary": "#6c757d",39    "bdg-light": "#f0f0f0",40    "bdg-dark": "#343a40",41}42 43_BADGE_RE = re.compile(r":(bdg-[a-z]+):`([^`]+)`")44_VERSIONADDED_RE = re.compile(r"^\.\. versionadded::\s*(.+)$", re.MULTILINE)45_VERSIONCHANGED_RE = re.compile(r"^\.\. versionchanged::\s*(.+)$", re.MULTILINE)46_CODE_BLOCK_RE = re.compile(r"^(\s*)\.\. code-block::\s*(\w+)?\s*$", re.MULTILINE)47 48 49def _replace_badges(text: str) -> str:50    """Convert :bdg-danger:`Foundation Model` to inline raw HTML."""51 52    def repl(match: re.Match) -> str:53        cls, label = match.group(1), match.group(2)54        color = _BADGE_COLORS.get(cls, "#888")55        # Use rST raw HTML inline pass-through.56        return (57            f"\n\n.. raw:: html\n\n"58            f"   <span class=\"bd-badge\" style=\"background:{color};\">"59            f"{label}</span>\n\n"60        )61 62    return _BADGE_RE.sub(repl, text)63 64 65def _replace_versionadded(text: str) -> str:66    """Convert Sphinx versionadded/versionchanged to plain admonitions."""67    text = _VERSIONADDED_RE.sub(68        r".. note::\n\n   *New in version \1.*", text69    )70    text = _VERSIONCHANGED_RE.sub(71        r".. note::\n\n   *Changed in version \1.*", text72    )73    return text74 75 76def _normalize_code_block(text: str) -> str:77    """Convert `.. code-block:: python` to vanilla `.. code::` (docutils-OK)."""78 79    def repl(match: re.Match) -> str:80        indent, lang = match.group(1), match.group(2) or ""81        return f"{indent}.. code:: {lang}".rstrip()82 83    return _CODE_BLOCK_RE.sub(repl, text)84 85 86def _strip_unsupported_directives(text: str) -> str:87    """Drop directives that docutils cannot parse and we do not want rendered.88 89    Currently: rubric (treated as a small heading) and bibliography-only items.90    """91    text = re.sub(r"^\.\. rubric::\s*(.+)$", r"\n**\1**\n", text, flags=re.MULTILINE)92    return text93 94 95def preprocess_docstring(doc: str) -> str:96    """Apply all rST → docutils-friendly transformations."""97    if not doc:98        return ""99    doc = dedent(doc)100    doc = _replace_badges(doc)101    doc = _replace_versionadded(doc)102    doc = _normalize_code_block(doc)103    doc = _strip_unsupported_directives(doc)104    return doc105 106 107# All visual styling now lives in app.py's GLOBAL_CSS so it's injected108# once via Blocks(css=...) instead of being re-emitted on every model109# switch. The renderer just returns the structural HTML wrapped in110# .bd-doc.111 112 113def render_docstring_html(doc: str | None) -> str:114    """Render an rST docstring to an HTML fragment wrapped in ``.bd-doc``.115 116    Styling is supplied by app.py's GLOBAL_CSS — this function emits117    structural HTML only. Failures fall back to a ``<pre>`` dump so the118    Space never blanks out.119    """120    if not doc:121        return "<div class='bd-doc'><em>No docstring available.</em></div>"122 123    processed = preprocess_docstring(doc)124    try:125        parts = publish_parts(126            source=processed,127            writer_name="html5",128            settings_overrides={129                "report_level": 5,  # suppress all docutils warnings130                "halt_level": 5,131                "embed_stylesheet": False,132                "input_encoding": "unicode",133                "output_encoding": "unicode",134                "doctitle_xform": False,135                "initial_header_level": 2,136            },137        )138        body = parts["html_body"]139    except SystemMessage as exc:  # pragma: no cover — defensive140        body = f"<pre>{processed}</pre><p><em>(rST parse error: {exc})</em></p>"141 142    return f"<div class='bd-doc'>{body}</div>"143 144 145def get_signature_str(cls: type) -> str:146    """Return the formatted __init__ signature for display."""147    try:148        sig = inspect.signature(cls.__init__)149        return f"{cls.__name__}{sig}"150    except (ValueError, TypeError):151        return f"{cls.__name__}(...)"152 153 154def get_source_link(cls: type, branch: str = "master") -> str | None:155    """Return a github link to the class definition."""156    try:157        module = inspect.getmodule(cls)158        if module is None or not hasattr(module, "__file__"):159            return None160        rel_path = module.__file__.split("braindecode/", 1)[-1]161        rel_path = "braindecode/" + rel_path162        try:163            _, lineno = inspect.getsourcelines(cls)164        except OSError:165            lineno = 1166        return (167            f"https://github.com/braindecode/braindecode/blob/{branch}/"168            f"{rel_path}#L{lineno}"169        )170    except Exception:171        return None172