import re import requests import nbformat RAW_BASE = "https://raw.githubusercontent.com/Qiskit/textbook/main/notebooks/intro/" README_URL = RAW_BASE + "README.md" FALLBACK = [ "what-is-quantum.ipynb", "entangled-states.ipynb", "superdense-coding.ipynb", "teleportation.ipynb", ] # ──────────────────────────────────────────────────────────────────── # Internal helpers # ──────────────────────────────────────────────────────────────────── def _discover_notebooks() -> list[str]: """Scrape notebooks/intro/README.md for *.ipynb links; fallback if offline.""" try: md = requests.get(README_URL, timeout=10).text found = re.findall(r"\(([^)]+?\.ipynb)\)", md) if found: return found except requests.RequestException: pass return FALLBACK def _pretty(name: str) -> str: """'superdense-coding.ipynb' ➜ 'Superdense Coding'.""" return name.replace("-", " ").replace(".ipynb", "").title() # ──────────────────────────────────────────────────────────────────── # Public API # ──────────────────────────────────────────────────────────────────── def get_theory_topics() -> dict[str, str]: """ Return a mapping of *friendly topic name* → *notebook filename*. Example ------- {'What Is Quantum?': 'what-is-quantum.ipynb', ...} """ return {_pretty(f): f for f in _discover_notebooks()} def get_theory( topic: str, markdown_only: bool = True, include_headers: bool = True, ) -> str: """ Download **one** intro notebook and return its content as text. Parameters ---------- topic Accepts pretty title (“Teleportation”), slug (“teleportation”) or exact filename (“teleportation.ipynb”). markdown_only True (default) ➜ keep only Markdown cells; False ➜ include code cells fenced as ```python. include_headers Prepend an H1 title for readability. Raises ------ ValueError If *topic* cannot be resolved. Returns ------- str Concatenated notebook text. """ topics = get_theory_topics() # Build a lenient lookup table lookup: dict[str, str] = {} for pretty, fname in topics.items(): slug = fname.removesuffix(".ipynb") lookup[pretty.lower()] = fname lookup[slug.lower()] = fname lookup[fname.lower()] = fname key = topic.lower() if key not in lookup: raise ValueError( f"Unknown topic '{topic}'. " f"Known: {', '.join(topics.keys())}." ) fname = lookup[key] raw_json = requests.get(RAW_BASE + fname, timeout=20).text nb = nbformat.reads(raw_json, as_version=4) parts: list[str] = [] if include_headers: parts.append(f"# {_pretty(fname)}\n") for cell in nb.cells: if cell.cell_type == "markdown": parts.append(cell.source) elif cell.cell_type == "code" and not markdown_only: parts.append(f"```python\n{cell.source}\n```") return "\n\n".join(parts)