version_check.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #!/usr/bin/env python
  2. import importlib.util
  3. import os
  4. import pkgutil
  5. import re
  6. import runpy
  7. import subprocess
  8. import sys
  9. import docutils.nodes
  10. import docutils.parsers.rst
  11. import docutils.utils
  12. import docutils.frontend
  13. CHANGELOG_PATTERN = re.compile(r"^Version (\S+)((?: \(.+\)))?$")
  14. def parse_rst(text: str) -> docutils.nodes.document:
  15. parser = docutils.parsers.rst.Parser()
  16. components = (docutils.parsers.rst.Parser,)
  17. settings = docutils.frontend.OptionParser(
  18. components=components
  19. ).get_default_values()
  20. document = docutils.utils.new_document("<rst-doc>", settings=settings)
  21. parser.parse(text, document)
  22. return document
  23. class SectionVisitor(docutils.nodes.NodeVisitor):
  24. def __init__(self, *args, **kwargs):
  25. super().__init__(*args, **kwargs)
  26. self.sectiontitles_found = []
  27. def visit_section(self, node: docutils.nodes.section) -> None:
  28. """Called for "section" nodes."""
  29. title = node[0]
  30. assert isinstance(title, docutils.nodes.title)
  31. self.sectiontitles_found.append(title.astext())
  32. def unknown_visit(self, node: docutils.nodes.Node) -> None:
  33. """Called for all other node types."""
  34. pass
  35. def get_sphinxchangelog_version(rootdir):
  36. with open(os.path.join(rootdir, "docs", "changelog.rst"), mode="r") as f:
  37. doc = parse_rst(f.read())
  38. visitor = SectionVisitor(doc)
  39. doc.walk(visitor)
  40. unique_sectiontitles = set(visitor.sectiontitles_found)
  41. assert len(visitor.sectiontitles_found) == len(unique_sectiontitles)
  42. assert visitor.sectiontitles_found[0] == "Changelog"
  43. matchobj = CHANGELOG_PATTERN.match(visitor.sectiontitles_found[1])
  44. assert matchobj
  45. version = matchobj.group(1)
  46. version_date = matchobj.group(2)
  47. matchobj = CHANGELOG_PATTERN.match(visitor.sectiontitles_found[2])
  48. assert matchobj
  49. release = matchobj.group(1)
  50. release_date = matchobj.group(2)
  51. if version_date:
  52. assert version_date == release_date
  53. return version, release
  54. def get_sphinxconfpy_version(rootdir):
  55. """Get version from Sphinx' conf.py."""
  56. sphinx_conf = runpy.run_path(os.path.join(rootdir, "docs", "conf.py"))
  57. version, sep, bugfix = sphinx_conf["release"].rpartition(".")
  58. assert sep == "."
  59. assert bugfix
  60. assert version == sphinx_conf["version"]
  61. return sphinx_conf["version"], sphinx_conf["release"]
  62. def get_setuppy_version(rootdir):
  63. """Get version from setup.py."""
  64. setupfile = os.path.join(rootdir, "setup.py")
  65. cmd = (sys.executable, setupfile, "--version")
  66. release = subprocess.check_output(cmd).decode().rstrip(os.linesep)
  67. version = release.rpartition(".")[0]
  68. return version, release
  69. def get_package_version(rootdir):
  70. """Get version from package __init__.py."""
  71. sys.path.insert(0, os.path.join(rootdir))
  72. for modinfo in pkgutil.walk_packages(path=[rootdir]):
  73. if modinfo.ispkg and modinfo.name == "sphinx_multiversion":
  74. break
  75. else:
  76. raise FileNotFoundError("package not found")
  77. spec = modinfo.module_finder.find_spec(modinfo.name)
  78. mod = importlib.util.module_from_spec(spec)
  79. spec.loader.exec_module(mod)
  80. release = mod.__version__
  81. version = release.rpartition(".")[0]
  82. return version, release
  83. def main():
  84. rootdir = os.path.join(os.path.dirname(__file__), "..")
  85. setuppy_version, setuppy_release = get_setuppy_version(rootdir)
  86. package_version, package_release = get_package_version(rootdir)
  87. confpy_version, confpy_release = get_sphinxconfpy_version(rootdir)
  88. changelog_version, changelog_release = get_sphinxchangelog_version(rootdir)
  89. version_head = "Version"
  90. version_width = max(
  91. (
  92. len(repr(x))
  93. for x in (
  94. version_head,
  95. setuppy_version,
  96. package_version,
  97. confpy_version,
  98. changelog_version,
  99. )
  100. )
  101. )
  102. release_head = "Release"
  103. release_width = max(
  104. (
  105. len(repr(x))
  106. for x in (
  107. release_head,
  108. setuppy_release,
  109. package_release,
  110. confpy_release,
  111. changelog_release,
  112. )
  113. )
  114. )
  115. print(
  116. f"File {version_head} {release_head}\n"
  117. f"------------------------------- {'-' * version_width}"
  118. f" {'-' * release_width}\n"
  119. f"setup.py "
  120. f" {setuppy_version!r:>{version_width}}"
  121. f" {setuppy_release!r:>{release_width}}\n"
  122. f"sphinx_multiversion/__init__.py"
  123. f" {package_version!r:>{version_width}}"
  124. f" {package_release!r:>{release_width}}\n"
  125. f"docs/conf.py "
  126. f" {confpy_version!r:>{version_width}}"
  127. f" {confpy_release!r:>{release_width}}\n"
  128. f"docs/changelog.rst "
  129. f" {changelog_version!r:>{version_width}}"
  130. f" {changelog_release!r:>{release_width}}\n"
  131. )
  132. assert setuppy_version == confpy_version
  133. assert setuppy_version == package_version
  134. assert setuppy_version == changelog_version
  135. assert setuppy_release == confpy_release
  136. assert setuppy_release == package_release
  137. assert setuppy_release == changelog_release
  138. if __name__ == "__main__":
  139. main()