diff --git a/docs/conf.py b/docs/conf.py index d3d997397..3f2a60bcb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,32 +3,32 @@ import re import sys from datetime import date, datetime +from importlib.machinery import SourceFileLoader from pathlib import Path from subprocess import check_output -from typing import Any, cast +from typing import Any -from docutils.nodes import Element, Node, Text, container, fully_normalize_name, literal, paragraph, reference, strong -from docutils.parsers.rst.directives import flag, unchanged, unchanged_required -from docutils.parsers.rst.states import RSTState, RSTStateMachine -from docutils.statemachine import StringList, string2lines +from docutils.nodes import Element, reference from sphinx.addnodes import pending_xref from sphinx.application import Sphinx from sphinx.builders import Builder -from sphinx.domains.std import StandardDomain +from sphinx.domains.python import PythonDomain from sphinx.environment import BuildEnvironment from sphinx.ext.autodoc import Options from sphinx.ext.extlinks import ExternalLinksChecker -from sphinx.locale import __ -from sphinx.util.docutils import SphinxDirective -from sphinx.util.logging import getLogger from tox import __version__ -company = "tox-dev" -name = "tox" -version = ".".join(__version__.split(".")[:2]) -release = __version__ +company, name = "tox-dev", "tox" +release, version = __version__, ".".join(__version__.split(".")[:2]) copyright = f"2010-{date.today().year}, {company}" +master_doc, source_suffix = "index", ".rst" + +html_theme = "furo" +html_title, html_last_updated_fmt = "tox", datetime.now().isoformat() +pygments_style, pygments_dark_style = "sphinx", "monokai" +html_static_path, html_css_files = ["_static"], ["custom.css"] +html_logo, html_favicon = "_static/img/tox.svg", "_static/img/toxfavi.ico" extensions = [ "sphinx.ext.autodoc", @@ -41,38 +41,13 @@ "sphinx_copybutton", ] -templates_path = [] -unused_docs = [] -source_suffix = ".rst" exclude_patterns = ["_build", "changelog/*", "_draft.rst"] - -master_doc = "index" -pygments_style = "default" - -project = name -today_fmt = "%B %d, %Y" - -html_theme = "furo" -html_theme_options = { - "navigation_with_keys": True, -} -html_title = "tox 4 - rewrite" -html_static_path = ["_static"] -html_css_files = ["custom.css"] -html_last_updated_fmt = datetime.now().isoformat() -html_logo = "_static/img/tox.svg" -html_favicon = "_static/img/toxfavi.ico" - -autoclass_content = "class" -autodoc_member_order = "bysource" +autoclass_content, autodoc_member_order, autodoc_typehints = "class", "bysource", "none" autodoc_default_options = { "member-order": "bysource", "undoc-members": True, "show-inheritance": True, } -autodoc_typehints = "none" -always_document_param_types = False -typehints_fully_qualified = True autosectionlabel_prefix_document = True extlinks = { @@ -95,10 +70,6 @@ extlinks_detect_hardcoded_links = True -def skip_member(app: Sphinx, what: str, name: str, obj: Any, would_skip: bool, options: Options) -> bool: # noqa: U100 - return name in options.get("exclude-members", set()) or would_skip - - def process_signature( app: Sphinx, # noqa: U100 objtype: str, @@ -113,16 +84,13 @@ def process_signature( def setup(app: Sphinx) -> None: - logger = getLogger(__name__) - - root = Path(__file__).parents[1] - exe = Path(sys.executable) + here = Path(__file__).parent + # 1. run towncrier + root, exe = here.parent, Path(sys.executable) towncrier = exe.with_name(f"towncrier{exe.suffix}") new = check_output([str(towncrier), "--draft", "--version", "NEXT"], cwd=root, universal_newlines=True) (root / "docs" / "_draft.rst").write_text("" if "No significant changes" in new else new) - from sphinx.domains.python import PythonDomain - class PatchedPythonDomain(PythonDomain): def resolve_xref( self, @@ -148,95 +116,10 @@ def resolve_xref( # node.children[0].children[0] = Text(target, target) return super().resolve_xref(env, fromdocname, builder, type, target, node, contnode) - app.connect("autodoc-skip-member", skip_member) app.connect("autodoc-process-signature", process_signature, priority=400) app.add_domain(PatchedPythonDomain, override=True) - - class ToxConfig(SphinxDirective): - name = "conf" - has_content = True - option_spec = { - "keys": unchanged_required, - "version_added": unchanged, - "version_changed": unchanged, - "default": unchanged, - "constant": flag, - "ref_suffix": unchanged, - } - - def __init__( - self, - name: str, - arguments: list[str], - options: dict[str, str], - content: StringList, - lineno: int, - content_offset: int, - block_text: str, - state: RSTState, - state_machine: RSTStateMachine, - ): - super().__init__( - name, - arguments, - options, - content, - lineno, - content_offset, - block_text, - state, - state_machine, - ) - self._std_domain: StandardDomain = cast(StandardDomain, self.env.get_domain("std")) - - def run(self) -> list[Node]: - self.env.note_reread() # this document needs to be always updated - - line = paragraph() - line += Text("■" if "constant" in self.options else "⚙️") - for key in (i.strip() for i in self.options["keys"].split(",")): - line += Text(" ") - self._mk_key(line, key) - if "default" in self.options: - default = self.options["default"] - line += Text(" with default value of ") - line += literal(default, default) - if "version_added" in self.options: - line += Text(" 📢 added in ") - ver = self.options["version_added"] - line += literal(ver, ver) - - p = container("") - self.state.nested_parse(StringList(string2lines("\n".join(f" {i}" for i in self.content))), 0, p) - line += p - - return [line] - - def _mk_key(self, line: paragraph, key: str) -> None: - ref_id = key if "ref_suffix" not in self.options else f"{key}-{self.options['ref_suffix']}" - ref = reference("", refid=ref_id, reftitle=key) - line.attributes["ids"].append(ref_id) - st = strong() - st += literal(text=key) - ref += st - self._register_ref(ref_id, ref_id, ref) - line += ref - - def _register_ref(self, ref_name: str, ref_title: str, node: Element) -> None: - of_name, doc_name = fully_normalize_name(ref_name), self.env.docname - if of_name in self._std_domain.labels: - logger.warning( - __("duplicate label %s, other instance in %s"), - of_name, - self.env.doc2path(self._std_domain.labels[of_name][0]), - location=node, - type="sphinx-argparse-cli", - subtype=self.env.docname, - ) - self._std_domain.anonlabels[of_name] = doc_name, ref_name - self._std_domain.labels[of_name] = doc_name, ref_name, ref_title - - app.add_directive(ToxConfig.name, ToxConfig) + tox_cfg = SourceFileLoader("tox_conf", str(here / "tox_conf.py")).load_module().ToxConfig + app.add_directive(tox_cfg.name, tox_cfg) def check_uri(self, refnode: reference) -> None: if refnode.document.attributes["source"].endswith("index.rst"): diff --git a/docs/tox_conf.py b/docs/tox_conf.py new file mode 100644 index 000000000..e5134ff84 --- /dev/null +++ b/docs/tox_conf.py @@ -0,0 +1,99 @@ +from __future__ import annotations + +from typing import cast + +from docutils.nodes import Element, Node, Text, container, fully_normalize_name, literal, paragraph, reference, strong +from docutils.parsers.rst.directives import flag, unchanged, unchanged_required +from docutils.parsers.rst.states import RSTState, RSTStateMachine +from docutils.statemachine import StringList, string2lines +from sphinx.domains.std import StandardDomain +from sphinx.locale import __ +from sphinx.util.docutils import SphinxDirective +from sphinx.util.logging import getLogger + +LOGGER = getLogger(__name__) + + +class ToxConfig(SphinxDirective): + name = "conf" + has_content = True + option_spec = { + "keys": unchanged_required, + "version_added": unchanged, + "version_changed": unchanged, + "default": unchanged, + "constant": flag, + "ref_suffix": unchanged, + } + + def __init__( + self, + name: str, + arguments: list[str], + options: dict[str, str], + content: StringList, + lineno: int, + content_offset: int, + block_text: str, + state: RSTState, + state_machine: RSTStateMachine, + ): + super().__init__( + name, + arguments, + options, + content, + lineno, + content_offset, + block_text, + state, + state_machine, + ) + self._std_domain: StandardDomain = cast(StandardDomain, self.env.get_domain("std")) + + def run(self) -> list[Node]: + self.env.note_reread() # this document needs to be always updated + + line = paragraph() + line += Text("■" if "constant" in self.options else "⚙️") + for key in (i.strip() for i in self.options["keys"].split(",")): + line += Text(" ") + self._mk_key(line, key) + if "default" in self.options: + default = self.options["default"] + line += Text(" with default value of ") + line += literal(default, default) + if "version_added" in self.options: + line += Text(" 📢 added in ") + ver = self.options["version_added"] + line += literal(ver, ver) + + p = container("") + self.state.nested_parse(StringList(string2lines("\n".join(f" {i}" for i in self.content))), 0, p) + line += p + + return [line] + + def _mk_key(self, line: paragraph, key: str) -> None: + ref_id = key if "ref_suffix" not in self.options else f"{key}-{self.options['ref_suffix']}" + ref = reference("", refid=ref_id, reftitle=key) + line.attributes["ids"].append(ref_id) + st = strong() + st += literal(text=key) + ref += st + self._register_ref(ref_id, ref_id, ref) + line += ref + + def _register_ref(self, ref_name: str, ref_title: str, node: Element) -> None: + of_name, doc_name = fully_normalize_name(ref_name), self.env.docname + if of_name in self._std_domain.labels: + LOGGER.warning( + __("duplicate label %s, other instance in %s"), + of_name, + self.env.doc2path(self._std_domain.labels[of_name][0]), + location=node, + type="sphinx-argparse-cli", + subtype=self.env.docname, + ) + self._std_domain.anonlabels[of_name] = doc_name, ref_name + self._std_domain.labels[of_name] = doc_name, ref_name, ref_title diff --git a/setup.cfg b/setup.cfg index c3653533b..e6a4ecb24 100644 --- a/setup.cfg +++ b/setup.cfg @@ -60,31 +60,31 @@ console_scripts = [options.extras_require] docs = - furo>=2021.8.17b43 + furo>=2022.1.2 sphinx>=4.4 - sphinx-argparse-cli>=1.7 + sphinx-argparse-cli>=1.8.3 sphinx-autodoc-typehints>=1.16 - sphinx-copybutton>=0.4 - sphinx-inline-tabs>=2021.4.11b9 - sphinxcontrib-towncrier>=0.2.0a0 + sphinx-copybutton>=0.5 + sphinx-inline-tabs>=2022.1.2b11 + sphinxcontrib-towncrier>=0.2.1a0 towncrier>=21.3 testing = - covdefaults>=1.2 + covdefaults>=2.2 devpi-client>=5.2 - devpi-server>=6.1 - distlib>=0.3.2 - filelock>=3 + devpi-server>=6.4 + distlib>=0.3.4 + filelock>=3.4 flaky>=3.7 freezegun>=1.1 - psutil>=5.8 - pytest>=6.2 - pytest-cov>=2.12 - pytest-mock>=3.6 - pytest-xdist>=2.3 + psutil>=5.9 + pytest>=7 + pytest-cov>=3 + pytest-mock>=3.7 + pytest-xdist>=2.5 re-assert>=1.1 - setuptools>=57 + setuptools>=60 setuptools-scm>=6 - wheel>=0.36 + wheel>=0.37 [options.package_data] tox = py.typed