"""Markup and format conversion utilities. Pure utility layer for text processing across help and docs systems. """ import io from typing import TYPE_CHECKING, Any, Optional if TYPE_CHECKING: from rich.console import Console def extract_text(obj: Any, console: Optional["Console"] = None, preserve_markup: bool = False) -> str: """Extract text from Rich renderables or any object. Parameters ---------- obj : Any Object to convert to text. console : Console | None Console for rendering Rich objects. preserve_markup : bool If True, preserve original markdown/RST markup when available. When False, always render to plain text. Returns ------- str Text representation (plain or with markup preserved). """ if obj is None: return "" if hasattr(obj, "primary_renderable"): primary = getattr(obj, "primary_renderable", None) if primary is not None: if preserve_markup and hasattr(primary, "markup"): return primary.markup.rstrip() return extract_text(primary, console, preserve_markup=preserve_markup) if hasattr(obj, "plain"): return obj.plain.rstrip() if preserve_markup and hasattr(obj, "markup"): return obj.markup.rstrip() if hasattr(obj, "__rich_console__"): from rich.console import Console plain_console = Console( file=io.StringIO(), width=console.width if console else 120, force_terminal=False, no_color=True, highlight=False, markup=False, emoji=False, ) with plain_console.capture() as capture: plain_console.print(obj, end="") return capture.get().rstrip() return str(obj).rstrip() def escape_rst(text: str | None) -> str: """Escape special reStructuredText characters in text. Parameters ---------- text : str | None Text to escape. Can be None. Returns ------- str Escaped text safe for RST. """ if not text: return "" return text.replace("\\", "\\\\") def escape_markdown(text: str | None) -> str | None: """Escape special markdown characters in text. If the text appears to already contain markdown formatting (bold, italic, code, links, or headings), it is returned unchanged. Otherwise, pipe characters are escaped for table compatibility. Parameters ---------- text : str | None Text to escape. Can be None. Returns ------- str | None Escaped text safe for markdown, or None if input was None. """ if not text: return text if any(pattern in text for pattern in ["**", "``", "`", "](", "#"]): return text text = text.replace("|", "\\|") return text def escape_html(text: str | None) -> str: """Escape special HTML characters in text. Parameters ---------- text : str | None Text to escape. Can be None. Returns ------- str Escaped text safe for HTML. """ if not text: return "" text = text.replace("&", "&") text = text.replace("<", "<") text = text.replace(">", ">") text = text.replace('"', """) text = text.replace("'", "'") return text