123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- # -*- coding: utf-8 -*-
- import datetime
- import json
- import collections
- import logging
- import os
- import posixpath
- from sphinx import config as sphinx_config
- from sphinx.util import i18n as sphinx_i18n
- from sphinx.locale import _
- logger = logging.getLogger(__name__)
- DATE_FMT = "%Y-%m-%d %H:%M:%S %z"
- DEFAULT_TAG_WHITELIST = r"^.*$"
- DEFAULT_BRANCH_WHITELIST = r"^.*$"
- DEFAULT_REMOTE_WHITELIST = None
- DEFAULT_RELEASED_PATTERN = r"^tags/.*$"
- DEFAULT_OUTPUTDIR_FORMAT = r"{ref.name}"
- Version = collections.namedtuple(
- "Version",
- [
- "name",
- "url",
- "version",
- "release",
- "is_released",
- ],
- )
- class VersionInfo:
- def __init__(self, app, context, metadata, current_version_name):
- self.app = app
- self.context = context
- self.metadata = metadata
- self.current_version_name = current_version_name
- def _dict_to_versionobj(self, v):
- return Version(
- name=v["name"],
- url=self.vpathto(v["name"]),
- version=v["version"],
- release=v["release"],
- is_released=v["is_released"],
- )
- @property
- def tags(self):
- return [
- self._dict_to_versionobj(v)
- for v in self.metadata.values()
- if v["source"] == "tags"
- ]
- @property
- def branches(self):
- return [
- self._dict_to_versionobj(v)
- for v in self.metadata.values()
- if v["source"] != "tags"
- ]
- @property
- def releases(self):
- return [
- self._dict_to_versionobj(v)
- for v in self.metadata.values()
- if v["is_released"]
- ]
- @property
- def in_development(self):
- return [
- self._dict_to_versionobj(v)
- for v in self.metadata.values()
- if not v["is_released"]
- ]
- def __iter__(self):
- for item in self.tags:
- yield item
- for item in self.branches:
- yield item
- def __getitem__(self, name):
- v = self.metadata.get(name)
- if v:
- return self._dict_to_versionobj(v)
- def vhasdoc(self, other_version_name):
- if self.current_version_name == other_version_name:
- return True
- other_version = self.metadata[other_version_name]
- return self.context["pagename"] in other_version["docnames"]
- def vpathto(self, other_version_name):
- if self.current_version_name == other_version_name:
- return "{}.html".format(
- posixpath.split(self.context["pagename"])[-1]
- )
- # Find relative outputdir paths from common output root
- current_version = self.metadata[self.current_version_name]
- other_version = self.metadata[other_version_name]
- current_outputroot = os.path.abspath(current_version["outputdir"])
- other_outputroot = os.path.abspath(other_version["outputdir"])
- outputroot = os.path.commonpath((current_outputroot, other_outputroot))
- current_outputroot = os.path.relpath(
- current_outputroot, start=outputroot
- )
- other_outputroot = os.path.relpath(other_outputroot, start=outputroot)
- # Ensure that we use POSIX separators in the path (for the HTML code)
- if os.sep != posixpath.sep:
- current_outputroot = posixpath.join(
- *os.path.split(current_outputroot)
- )
- other_outputroot = posixpath.join(*os.path.split(other_outputroot))
- # Find relative path to root of other_version's outputdir
- current_outputdir = posixpath.dirname(
- posixpath.join(current_outputroot, self.context["pagename"])
- )
- other_outputdir = posixpath.relpath(
- other_outputroot, start=current_outputdir
- )
- if not self.vhasdoc(other_version_name):
- return posixpath.join(other_outputdir, "index.html")
- return posixpath.join(
- other_outputdir, "{}.html".format(self.context["pagename"])
- )
- def html_page_context(app, pagename, templatename, context, doctree):
- versioninfo = VersionInfo(
- app, context, app.config.smv_metadata, app.config.smv_current_version
- )
- context["versions"] = versioninfo
- context["vhasdoc"] = versioninfo.vhasdoc
- context["vpathto"] = versioninfo.vpathto
- context["current_version"] = versioninfo[app.config.smv_current_version]
- context["latest_version"] = versioninfo[app.config.smv_latest_version]
- context["html_theme"] = app.config.html_theme
- def config_inited(app, config):
- """Update the Sphinx builder.
- :param sphinx.application.Sphinx app: Sphinx application object.
- """
- if not config.smv_metadata:
- if not config.smv_metadata_path:
- return
- with open(config.smv_metadata_path, mode="r") as f:
- metadata = json.load(f)
- config.smv_metadata = metadata
- if not config.smv_current_version:
- return
- try:
- data = app.config.smv_metadata[config.smv_current_version]
- except KeyError:
- return
- app.connect("html-page-context", html_page_context)
- # Restore config values
- old_config = sphinx_config.Config.read(data["confdir"])
- old_config.pre_init_values()
- old_config.init_values()
- config.version = data["version"]
- config.release = data["release"]
- config.rst_prolog = data["rst_prolog"]
- config.today = old_config.today
- if not config.today:
- config.today = sphinx_i18n.format_date(
- format=config.today_fmt or _("%b %d, %Y"),
- date=datetime.datetime.strptime(data["creatordate"], DATE_FMT),
- language=config.language,
- )
- def setup(app):
- app.add_config_value("smv_metadata", {}, "html")
- app.add_config_value("smv_metadata_path", "", "html")
- app.add_config_value("smv_current_version", "", "html")
- app.add_config_value("smv_latest_version", "master", "html")
- app.add_config_value("smv_tag_whitelist", DEFAULT_TAG_WHITELIST, "html")
- app.add_config_value(
- "smv_branch_whitelist", DEFAULT_BRANCH_WHITELIST, "html"
- )
- app.add_config_value(
- "smv_remote_whitelist", DEFAULT_REMOTE_WHITELIST, "html"
- )
- app.add_config_value(
- "smv_released_pattern", DEFAULT_RELEASED_PATTERN, "html"
- )
- app.add_config_value(
- "smv_outputdir_format", DEFAULT_OUTPUTDIR_FORMAT, "html"
- )
- app.connect("config-inited", config_inited)
- return {
- "version": "0.2",
- "parallel_read_safe": True,
- "parallel_write_safe": True,
- }
|