File: C:/Users/fred/anaconda3/Lib/site-packages/bokeh/sphinxext/bokeh_gallery.py
# -----------------------------------------------------------------------------
# Copyright (c) Anaconda, Inc., and Bokeh Contributors.
# All rights reserved.
#
# The full license is in the file LICENSE.txt, distributed with this software.
# -----------------------------------------------------------------------------
""" Generate a gallery of Bokeh plots from a configuration file.
To enable this extension, add `"bokeh.sphinxext.bokeh_gallery"` to the
extensions list in your Sphinx configuration module.
"""
# -----------------------------------------------------------------------------
# Boilerplate
# -----------------------------------------------------------------------------
from __future__ import annotations
import logging # isort:skip
log = logging.getLogger(__name__)
# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
# Standard library imports
import json
import os
from os.path import (
abspath,
dirname,
exists,
getmtime,
isdir,
isfile,
join,
)
from pathlib import PurePath
from typing import TypedDict
# External imports
from sphinx.errors import SphinxError
from sphinx.util import ensuredir
from sphinx.util.display import status_iterator
# Bokeh imports
from . import PARALLEL_SAFE
from .bokeh_directive import BokehDirective
from .templates import GALLERY_DETAIL, GALLERY_PAGE
from .util import _REPO_TOP
# -----------------------------------------------------------------------------
# Globals and constants
# -----------------------------------------------------------------------------
__all__ = (
"BokehGalleryDirective",
"setup",
)
# -----------------------------------------------------------------------------
# General API
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Dev API
# -----------------------------------------------------------------------------
class GalleryDetail(TypedDict):
name: str
path: str
ref: str
rst_file_path: str
class BokehGalleryDirective(BokehDirective):
has_content = True
required_arguments = 0
def run(self):
docdir = dirname(self.env.doc2path(self.env.docname))
gallery_file = join(docdir, "gallery.json")
gallery_dir = join(dirname(dirname(gallery_file)), "gallery")
if not exists(gallery_dir) and isdir(gallery_dir):
raise SphinxError(f"gallery dir {gallery_dir!r} missing for gallery file {gallery_file!r}")
gallery_json = json.load(open(gallery_file))
opts = []
for location in self.content:
for detail in gallery_json[location]:
path = PurePath("examples") / location / detail["name"]
if "url" in detail:
url = detail.get("url")
target = "_blank"
else:
url = str(path.with_suffix(".html"))
target = None
opts.append({
"url": url,
"target": target,
"img": str(path.with_suffix("")),
"alt": detail.get("alt"),
"title": path.stem,
"desc": detail.get("desc", None),
})
rst_text = GALLERY_PAGE.render(opts=opts)
return self.parse(rst_text, "<bokeh-gallery>")
def config_inited_handler(app, config):
gallery_dir = join(app.srcdir, config.bokeh_gallery_dir)
examples_dir = join(app.srcdir, config.bokeh_examples_dir)
gallery_file = f"{gallery_dir}.json"
if not exists(gallery_file) and isfile(gallery_file):
raise SphinxError(f"could not find gallery file {gallery_file!r} for configured gallery dir {gallery_dir!r}")
gallery_file_mtime = getmtime(gallery_file)
ensuredir(gallery_dir)
ensuredir(examples_dir)
for subdir in config.bokeh_example_subdirs:
ensuredir(join(examples_dir, subdir))
# we will remove each file we process from this set and see if anything is
# left at the end (and remove it in that case)
extras = set(os.listdir(gallery_dir))
details = get_details(app)
details_iter = status_iterator(details, "creating gallery file entries... ", "brown", len(details), app.verbosity, stringify_func=lambda x: x["name"] + ".rst")
for detail in details_iter:
detail_file_path = join(examples_dir, detail["rst_file_path"])
if detail_file_path in extras:
extras.remove(detail_file_path)
# if the gallery detail file is newer than the gallery file, assume it is up-to-date
if exists(detail_file_path) and getmtime(detail_file_path) > gallery_file_mtime:
continue
with open(detail_file_path, "w") as f:
source_path = abspath(join(app.srcdir, "..", "..", "..", detail["path"]))
f.write(GALLERY_DETAIL.render(filename=detail["name"], source_path=source_path, ref=detail["ref"]))
for extra_file in extras:
os.remove(join(gallery_dir, extra_file))
def get_details(app):
details = []
for subdir in app.config.bokeh_example_subdirs:
for name in os.listdir(_REPO_TOP / "examples" / subdir):
path = PurePath("examples", subdir, name).as_posix()
if not name.startswith('_') and name.endswith('.py') and path not in app.config.bokeh_sampledata_xref_skiplist:
name = name.replace('.py', '')
rst_file_path = join(subdir, f'{name}.rst')
ref = f'.. _example_{name}_{subdir}:'
detail: GalleryDetail = {"path": path, "name": name, "rst_file_path": rst_file_path, "ref": ref}
details.append(detail)
return details
def setup(app):
""" Required Sphinx extension setup function. """
app.add_config_value("bokeh_gallery_dir", join("docs", "gallery"), "html")
app.add_config_value("bokeh_examples_dir", join("docs", "examples"), "html")
app.add_config_value("bokeh_example_subdirs", [], "html")
app.add_config_value("bokeh_sampledata_xref_skiplist", [], "html")
app.connect("config-inited", config_inited_handler)
app.add_directive("bokeh-gallery", BokehGalleryDirective)
return PARALLEL_SAFE
# -----------------------------------------------------------------------------
# Private API
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Code
# -----------------------------------------------------------------------------