from __future__ import annotations import json import os import textwrap from pathlib import Path from typing import Any from sphinx.addnodes import document from sphinx.application import Sphinx from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.environment import BuildEnvironment from mypy.main import define_options class MypyHTMLBuilder(StandaloneHTMLBuilder): strict_file: Path def __init__(self, app: Sphinx, env: BuildEnvironment) -> None: super().__init__(app, env) self._ref_to_doc = {} self.strict_file = Path(self.srcdir) / "strict_list.rst" self._add_strict_list() def write_doc(self, docname: str, doctree: document) -> None: super().write_doc(docname, doctree) self._ref_to_doc.update({_id: docname for _id in doctree.ids}) def _add_strict_list(self) -> None: strict_flags: list[str] _, strict_flags, _ = define_options() strict_part = ", ".join(f":option:`{s} `" for s in strict_flags) if ( not strict_part or strict_part.isspace() or len(strict_part) < 20 or len(strict_part) > 2000 ): raise ValueError(f"{strict_part=}, which doesn't look right (by a simple heuristic).") self.strict_file.write_text( "For this version of mypy, the list of flags enabled by strict is: " + strict_part ) def _verify_error_codes(self) -> None: from mypy.errorcodes import error_codes missing_error_codes = {c for c in error_codes if f"code-{c}" not in self._ref_to_doc} if missing_error_codes: raise ValueError( f"Some error codes are not documented: {', '.join(sorted(missing_error_codes))}" ) def _write_ref_redirector(self) -> None: if os.getenv("VERIFY_MYPY_ERROR_CODES"): self._verify_error_codes() p = Path(self.outdir) / "_refs.html" data = f""" """ p.write_text(textwrap.dedent(data)) def finish(self) -> None: super().finish() self._write_ref_redirector() self.strict_file.unlink() def setup(app: Sphinx) -> dict[str, Any]: app.add_builder(MypyHTMLBuilder, override=True) return {"version": "0.1", "parallel_read_safe": True, "parallel_write_safe": True}