#!/usr/bin/env python import argparse import asyncio import logging import re from pathlib import Path import git import httpx import urllib3 from tabulate import tabulate import build_docs logger = logging.getLogger(__name__) http = urllib3.PoolManager() VERSIONS = build_docs.parse_versions_from_devguide(http) LANGUAGES = build_docs.parse_languages_from_config() def parse_args(): parser = argparse.ArgumentParser( description="""Check the version of our build in different branches Hint: Use with | column -t""" ) parser.add_argument("cpython_clone", help="Path to a clone of CPython", type=Path) return parser.parse_args() def find_upstream_remote_name(repo: git.Repo) -> str: """Find a remote in the repo that matches the URL pattern.""" for remote in repo.remotes: for url in remote.urls: if "github.com/python" in url: return f"{remote.name}/" def find_sphinx_spec(text: str): if found := re.search( """sphinx[=<>~]{1,2}[0-9.]{3,}|needs_sphinx = [0-9.'"]*""", text, flags=re.IGNORECASE, ): return found.group(0).replace(" ", "") return "ø" def find_sphinx_in_files(repo: git.Repo, branch_or_tag, filenames): upstream = find_upstream_remote_name(repo) # Just in case you don't use upstream/: branch_or_tag = branch_or_tag.replace("upstream/", upstream) specs = [] for filename in filenames: try: blob = repo.git.show(f"{branch_or_tag}:{filename}") except git.exc.GitCommandError: specs.append("ø") else: specs.append(find_sphinx_spec(blob)) return specs CONF_FILES = { "travis": ".travis.yml", "requirements.txt": "Doc/requirements.txt", "conf.py": "Doc/conf.py", } def branch_or_tag_for(version): if version.status == "EOL": return f"tags/{version.branch_or_tag}" return f"upstream/{version.branch_or_tag}" def search_sphinx_versions_in_cpython(repo: git.Repo): repo.git.fetch("https://github.com/python/cpython") filenames = CONF_FILES.values() table = [ [ version.name, *find_sphinx_in_files(repo, branch_or_tag_for(version), filenames), ] for version in VERSIONS ] headers = ["version", *CONF_FILES.keys()] print(tabulate(table, headers=headers, tablefmt="rst", disable_numparse=True)) async def get_version_in_prod(language: str, version: str) -> str: if language == "en": url = f"https://docs.python.org/{version}/" else: url = f"https://docs.python.org/{language}/{version}/" async with httpx.AsyncClient() as client: try: response = await client.get(url, timeout=5) except httpx.ConnectTimeout: return "(timeout)" # Python 2.6--3.7: sphinx.pocoo.org # from Python 3.8: www.sphinx-doc.org if created_using := re.search( r"(?:sphinx.pocoo.org|www.sphinx-doc.org).*?([0-9.]+[0-9])", response.text ): return created_using.group(1) return "ø" async def which_sphinx_is_used_in_production(): table = [ [ version.name, *await asyncio.gather(*[ get_version_in_prod(language.tag, version.name) for language in LANGUAGES ]), ] for version in VERSIONS ] headers = ["version", *[language.tag for language in LANGUAGES]] print(tabulate(table, headers=headers, tablefmt="rst", disable_numparse=True)) def main(): logging.basicConfig(level=logging.INFO) logging.getLogger("charset_normalizer").setLevel(logging.WARNING) logging.getLogger("asyncio").setLevel(logging.WARNING) logging.getLogger("httpx").setLevel(logging.WARNING) args = parse_args() repo = git.Repo(args.cpython_clone) print("Sphinx configuration in various branches:", end="\n\n") search_sphinx_versions_in_cpython(repo) print() print("Sphinx build as seen on docs.python.org:", end="\n\n") asyncio.run(which_sphinx_is_used_in_production()) if __name__ == "__main__": main()