Source code for quantecon_book_theme.launch

from pathlib import Path
from typing import Any, Dict, Optional

from docutils.nodes import document
from sphinx.application import Sphinx
from sphinx.util import logging
from shutil import copy2


SPHINX_LOGGER = logging.getLogger(__name__)


[docs] def add_hub_urls( app: Sphinx, pagename: str, templatename: str, context: Dict[str, Any], doctree: Optional[document], ): """Builds a binder link and inserts it in HTML context for use in templating. This is a ``html-page-context`` sphinx event (see :ref:`sphinx:events`). :param pagename: The sphinx docname related to the page :param context: A dictionary of values that are given to the template engine, to render the page and can be modified to include custom values. :param doctree: A doctree when the page is created from a reST documents; it will be None when the page is created from an HTML template alone. """ # First decide if we'll insert any links path = app.env.doc2path(pagename) extension = Path(path).suffix # If so, insert the URLs depending on the configuration config_theme = app.config["html_theme_options"] launch_buttons = config_theme.get("launch_buttons", {}) if config_theme.get("nb_repository_url"): repo_url = _get_repo_url(config_theme) # Parse the repo parts from the URL org, repo, repo_subpath = _split_repo_url(repo_url) if repo_subpath: repo_url = repo_url.replace("/" + repo_subpath, "") repo_subpath += ( "/" # compatibility of code for cases which dont have this var ) if org is None and repo is None: # Skip the rest because the repo_url isn't right return # Construct the extra URL parts (app and relative path) notebook_interface_prefixes = {"classic": "tree", "jupyterlab": "lab/tree"} notebook_interface = launch_buttons.get("notebook_interface", "classic") if notebook_interface not in notebook_interface_prefixes: raise ValueError( ( "Notebook UI for Binder/JupyterHub links must be one" f"of {tuple(notebook_interface_prefixes.keys())}," f"not {notebook_interface}" ) ) ui_pre = notebook_interface_prefixes[notebook_interface] # Check if we have a non-ipynb file, but an ipynb of same name exists # If so, we'll use the ipynb extension instead of the text extension # if extension != ".ipynb" and Path(path).with_suffix(".ipynb").exists(): # extension = ".ipynb" extension = ".ipynb" # since we have nb_repo url # Construct a path to the file relative to the notebook repository root # Use nb_path_to_notebooks for notebook repo path (defaults to empty for flat repos) nb_relpath = config_theme.get("nb_path_to_notebooks", "").strip("/") if nb_relpath != "": nb_relpath += "/" # Strip path_to_docs from pagename since notebook repo structure may differ path_to_docs = config_theme.get("path_to_docs", "").strip("/") notebook_pagename = pagename if path_to_docs and pagename.startswith(path_to_docs + "/"): notebook_pagename = pagename[len(path_to_docs) + 1 :] path_rel_repo = f"{nb_relpath}{notebook_pagename}{extension}" branch = _get_branch(config_theme) # Now build infrastructure-specific links jupyterhub_url = launch_buttons.get("jupyterhub_url") binderhub_url = launch_buttons.get("binderhub_url") colab_url = launch_buttons.get("colab_url") context["launch_buttons"] = [] if binderhub_url: binderhub_url = ( config_theme["binderhub_url"] if "binderhub_url" in config_theme else "https://mybinder.org" ) context["binder_url"] = ( f"{binderhub_url}/v2/gh/{org}/{repo}/{branch}?" f"urlpath=tree/{repo_subpath}{ pagename }.ipynb" ) context["launch_buttons"].append( {"name": "BinderHub", "url": context["binder_url"]} ) urlpath = ui_pre + "/" + repo + "/" + repo_subpath + path_rel_repo url = ( f"{jupyterhub_url}/user-redirect/git-pull?" f"repo={repo_url}&urlpath={urlpath}" # noqa: E501 f"&branch={branch}" ) context["jupyterhub_url"] = url context["jupyterhub_urlpath"] = urlpath context["repo_branch"] = branch if jupyterhub_url: context["launch_buttons"].append( {"name": "JupyterHub", "url": context["jupyterhub_url"]} ) if colab_url: url = f"{colab_url}/github/{org}/{repo}/blob/{branch}/{repo_subpath}{path_rel_repo}" # noqa: E501 context["colab_url"] = url context["launch_buttons"].append( {"name": "Colab", "url": context["colab_url"]} ) # if multiple launch servers then show the dropdown setting icon if len(context["launch_buttons"]) == 1: context["default_server"] = context["launch_buttons"][0]["url"] else: context["default_server"] = context["colab_url"] if org is None and repo is None: # Skip the rest because the repo_url isn't right return if not launch_buttons or not _is_notebook(app, pagename): return # Check if we have a markdown notebook, and if so then add a link to the context if ( _is_notebook(app, pagename) and "sourcename" in context and context["sourcename"].endswith(".md") ): # Figure out the folders we want build_dir = Path(app.outdir).parent ntbk_dir = build_dir.joinpath("jupyter_execute") sources_dir = build_dir.joinpath("html", "_sources") # Paths to old and new notebooks path_ntbk = ntbk_dir.joinpath(pagename).with_suffix(".ipynb") path_new_notebook = sources_dir.joinpath(pagename).with_suffix(".ipynb") # Copy the notebook to `_sources` dir so it can be downloaded path_new_notebook.parent.mkdir(exist_ok=True, parents=True) copy2(path_ntbk, path_new_notebook) context["ipynb_source"] = pagename + ".ipynb" # Add thebe flag in context if launch_buttons.get("thebe", False): context["use_thebe"] = True
def _split_repo_url(url): """Split a repository URL into an org / repo combination.""" if "github.com/" in url: end = url.split("github.com/")[-1] org, repo = end.split("/")[:2] repo_subpath = "/".join(end.split("/")[2:]) else: SPHINX_LOGGER.warning( f"Currently Binder/JupyterHub repositories must be on GitHub, got {url}" ) org = repo = repo_subpath = None return org, repo, repo_subpath def _get_repo_url(config): repo_url = config.get("nb_repository_url") return repo_url def _is_notebook(app, pagename): return app.env.metadata[pagename].get("kernelspec") def _get_branch(config_theme): branch = config_theme.get("nb_branch") if not branch: branch = "main" return branch