sphinx.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. # -*- coding: utf-8 -*-
  2. import json
  3. import pathlib
  4. import collections
  5. import importlib.abc
  6. import logging
  7. import os
  8. import posixpath
  9. logger = logging.getLogger(__name__)
  10. DEFAULT_TAG_WHITELIST = r'^.*$'
  11. DEFAULT_BRANCH_WHITELIST = r'^.*$'
  12. DEFAULT_REMOTE_WHITELIST = None
  13. DEFAULT_OUTPUTDIR_FORMAT = r'{version.version}/{language}'
  14. Version = collections.namedtuple('Version', [
  15. 'name',
  16. 'url',
  17. 'version',
  18. 'release',
  19. ])
  20. class VersionInfo:
  21. def __init__(self, app, context, metadata, current_version_name):
  22. self.app = app
  23. self.context = context
  24. self.metadata = metadata
  25. self.current_version_name = current_version_name
  26. @property
  27. def tags(self):
  28. return [
  29. Version(
  30. name=v["name"],
  31. url=self.vpathto(v["name"]),
  32. version=v["version"],
  33. release=v["release"],
  34. ) for v in self.metadata.values() if v["source"] == "tags"
  35. ]
  36. @property
  37. def branches(self):
  38. return [
  39. Version(
  40. name=v["name"],
  41. url=self.vpathto(v["name"]),
  42. version=v["version"],
  43. release=v["release"],
  44. ) for v in self.metadata.values() if v["source"] != "tags"
  45. ]
  46. def __iter__(self):
  47. for item in self.tags:
  48. yield item
  49. for item in self.branches:
  50. yield item
  51. def __getitem__(self, name):
  52. v = self.metadata.get(name)
  53. if v:
  54. return Version(
  55. name=v["name"],
  56. url=self.vpathto(v["name"]),
  57. version=v["version"],
  58. release=v["release"],
  59. )
  60. def vhasdoc(self, other_version_name):
  61. if self.current_version_name == other_version_name:
  62. return True
  63. other_version = self.metadata[other_version_name]
  64. return self.context["pagename"] in other_version["docnames"]
  65. def vpathto(self, other_version_name):
  66. if self.current_version_name == other_version_name:
  67. return '{}.html'.format(
  68. posixpath.split(self.context["pagename"])[-1])
  69. # Find output root
  70. current_version = self.metadata[self.current_version_name]
  71. relpath = pathlib.PurePath(current_version["outputdir"])
  72. outputroot = os.path.join(
  73. *('..' for x in relpath.joinpath(self.context["pagename"]).parent.parts)
  74. )
  75. # Find output dir of other version
  76. other_version = self.metadata[other_version_name]
  77. outputdir = posixpath.join(outputroot, other_version["outputdir"])
  78. if not self.vhasdoc(other_version_name):
  79. return posixpath.join(outputdir, 'index.html')
  80. return posixpath.join(outputdir, '{}.html'.format(self.context["pagename"]))
  81. def parse_conf(config):
  82. module = {}
  83. code = importlib.abc.InspectLoader.source_to_code(config)
  84. exec(code, module)
  85. return module
  86. def format_outputdir(fmt, versionref, language):
  87. return fmt.format(version=versionref, language=language)
  88. def html_page_context(app, pagename, templatename, context, doctree):
  89. versioninfo = VersionInfo(
  90. app, context, app.config.smv_metadata, app.config.smv_current_version)
  91. context["versions"] = versioninfo
  92. context["vhasdoc"] = versioninfo.vhasdoc
  93. context["vpathto"] = versioninfo.vpathto
  94. context["current_version"] = versioninfo[app.config.smv_current_version]
  95. context["latest_version"] = versioninfo[app.config.smv_latest_version]
  96. context["html_theme"] = app.config.html_theme
  97. def config_inited(app, config):
  98. """Update the Sphinx builder.
  99. :param sphinx.application.Sphinx app: Sphinx application object.
  100. """
  101. if not config.smv_metadata:
  102. if not config.smv_metadata_path:
  103. return
  104. with open(config.smv_metadata_path, mode="r") as f:
  105. metadata = json.load(f)
  106. config.smv_metadata = metadata
  107. if not config.smv_current_version:
  108. return
  109. app.connect("html-page-context", html_page_context)
  110. # Restore config values
  111. conf_path = os.path.join(app.srcdir, "conf.py")
  112. with open(conf_path, mode="r") as f:
  113. conf = parse_conf(f.read())
  114. config.version = conf['version']
  115. config.release = conf['release']
  116. def setup(app):
  117. app.add_config_value("smv_metadata", {}, "html")
  118. app.add_config_value("smv_metadata_path", "", "html")
  119. app.add_config_value("smv_current_version", "", "html")
  120. app.add_config_value("smv_latest_version", "master", "html")
  121. app.add_config_value("smv_tag_whitelist", DEFAULT_TAG_WHITELIST, "html")
  122. app.add_config_value("smv_branch_whitelist", DEFAULT_BRANCH_WHITELIST, "html")
  123. app.add_config_value("smv_remote_whitelist", DEFAULT_REMOTE_WHITELIST, "html")
  124. app.add_config_value("smv_outputdir_format", DEFAULT_OUTPUTDIR_FORMAT, "html")
  125. app.connect("config-inited", config_inited)
  126. return {
  127. "version": "0.1",
  128. "parallel_read_safe": True,
  129. "parallel_write_safe": True,
  130. }