"""Render braindecode rST docstrings to HTML for the model-explorer Space. Braindecode docstrings use NumpyDoc + Sphinx extensions: .. figure:: (architecture image) :bdg-danger: (sphinx-design colored badges) .. versionadded:: (Sphinx admonition) .. important:: (admonition) .. code-block:: (highlighted code) [ref]_ (NumpyDoc citation reference) Pure docutils does not know about the Sphinx directives. Rather than spinning up a full Sphinx build inside the Space, we: 1. Pre-process the docstring with regex substitutions that map Sphinx-only directives to plain rST equivalents docutils can parse. 2. Hand the result to docutils.publish_parts for rST -> HTML. 3. Wrap with a small CSS style block that matches braindecode.org colors. This keeps the Space dependency-light (no sphinx at runtime) while still rendering figures, headings, parameter tables, and references. """ from __future__ import annotations import inspect import re from textwrap import dedent from docutils.core import publish_parts from docutils.utils import SystemMessage _BADGE_COLORS = { "bdg-danger": "#d9534f", "bdg-success": "#5cb85c", "bdg-primary": "#0072B2", "bdg-info": "#56B4E9", "bdg-warning": "#E69F00", "bdg-secondary": "#6c757d", "bdg-light": "#f0f0f0", "bdg-dark": "#343a40", } _BADGE_RE = re.compile(r":(bdg-[a-z]+):`([^`]+)`") _VERSIONADDED_RE = re.compile(r"^\.\. versionadded::\s*(.+)$", re.MULTILINE) _VERSIONCHANGED_RE = re.compile(r"^\.\. versionchanged::\s*(.+)$", re.MULTILINE) _CODE_BLOCK_RE = re.compile(r"^(\s*)\.\. code-block::\s*(\w+)?\s*$", re.MULTILINE) def _replace_badges(text: str) -> str: """Convert :bdg-danger:`Foundation Model` to inline raw HTML.""" def repl(match: re.Match) -> str: cls, label = match.group(1), match.group(2) color = _BADGE_COLORS.get(cls, "#888") # Use rST raw HTML inline pass-through. return ( f"\n\n.. raw:: html\n\n" f" {label}\n\n" ) return _BADGE_RE.sub(repl, text) def _replace_versionadded(text: str) -> str: """Convert Sphinx versionadded/versionchanged to plain admonitions.""" text = _VERSIONADDED_RE.sub( r".. note::\n\n *New in version \1.*", text ) text = _VERSIONCHANGED_RE.sub( r".. note::\n\n *Changed in version \1.*", text ) return text def _normalize_code_block(text: str) -> str: """Convert `.. code-block:: python` to vanilla `.. code::` (docutils-OK).""" def repl(match: re.Match) -> str: indent, lang = match.group(1), match.group(2) or "" return f"{indent}.. code:: {lang}".rstrip() return _CODE_BLOCK_RE.sub(repl, text) def _strip_unsupported_directives(text: str) -> str: """Drop directives that docutils cannot parse and we do not want rendered. Currently: rubric (treated as a small heading) and bibliography-only items. """ text = re.sub(r"^\.\. rubric::\s*(.+)$", r"\n**\1**\n", text, flags=re.MULTILINE) return text def preprocess_docstring(doc: str) -> str: """Apply all rST → docutils-friendly transformations.""" if not doc: return "" doc = dedent(doc) doc = _replace_badges(doc) doc = _replace_versionadded(doc) doc = _normalize_code_block(doc) doc = _strip_unsupported_directives(doc) return doc _STYLE = """ """ def render_docstring_html(doc: str | None) -> str: """Render an rST docstring to a self-contained HTML fragment. Returns a string with embedded