From 71840ccc4dcda11fc97a014c664426517f30f28d Mon Sep 17 00:00:00 2001 From: Brian Kohan Date: Thu, 22 Aug 2024 11:43:46 -0700 Subject: [PATCH] fix #39, fix #40 --- doc/source/changelog.rst | 6 ++++ pyproject.toml | 2 +- sphinxcontrib/typer/__init__.py | 56 +++++++++++++++++++++++++---- tests/tests.py | 35 ++++++++++++++++-- tests/typer/composite/repeat.rst | 1 + tests/typer/reference/cli-ref.py | 13 +++++++ tests/typer/reference/index.rst | 11 ++++++ tests/typer/reference/reference.rst | 8 +++++ 8 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 tests/typer/reference/cli-ref.py create mode 100644 tests/typer/reference/index.rst create mode 100644 tests/typer/reference/reference.rst diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst index 35a4809..381b950 100644 --- a/doc/source/changelog.rst +++ b/doc/source/changelog.rst @@ -2,6 +2,12 @@ Change Log ========== +v0.4.1 (21-AUG-2024) +==================== + +* Fixed `:typer: role does not work if processed before the definition. `_ +* Fixed `:typer: role link text does not reflect the actual command invocation. `_ + v0.4.0 (19-AUG-2024) ==================== diff --git a/pyproject.toml b/pyproject.toml index 027fb0a..e28db91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "sphinxcontrib-typer" -version = "0.4.0" +version = "0.4.1" description = "Auto generate docs for typer commands." authors = ["Brian Kohan "] license = "MIT" diff --git a/sphinxcontrib/typer/__init__.py b/sphinxcontrib/typer/__init__.py index e81fa81..ea97f18 100644 --- a/sphinxcontrib/typer/__init__.py +++ b/sphinxcontrib/typer/__init__.py @@ -33,6 +33,7 @@ from importlib import import_module from importlib.util import find_spec from pathlib import Path +from pprint import pformat import click from docutils import nodes @@ -42,6 +43,7 @@ from rich.console import Console from rich.theme import Theme from sphinx import application +from sphinx.addnodes import pending_xref from sphinx.util import logging from sphinx.util.nodes import make_refnode @@ -52,7 +54,7 @@ from typer.models import Context as TyperContext from typer.models import TyperInfo -VERSION = (0, 4, 0) +VERSION = (0, 4, 1) __title__ = "SphinxContrib Typer" __version__ = ".".join(str(i) for i in VERSION) @@ -458,7 +460,7 @@ def generate_nodes( self.env.domaindata["std"].setdefault("typer", {})[section_id] = ( self.env.docname, section_id, - " ".join(section_id.split("-")), + normal_cmd, ) # Summary @@ -584,7 +586,7 @@ def run(self) -> t.Iterable[nodes.section]: self.make_sections = "make-sections" in self.options self.nested = "show-nested" in self.options - self.prog_name = self.options.get("prog", None) + self.prog_name = self.options.get("prog", "") if "markup-mode" in self.options: self.markup_mode = self.options["markup-mode"] @@ -600,6 +602,8 @@ def run(self) -> t.Iterable[nodes.section]: "Unable to determine program name, please specify using " ":prog:" ) from err + self.prog_name = self.prog_name.strip() + self.width = self.options.get("width", 65) self.iframe_height = self.options.get("iframe-height", None) @@ -950,6 +954,33 @@ def typer_convert_png( im.save(str(png_path)) # Saves the screenshot +def resolve_typer_reference(app, env, node, contnode): + target_id = node["reftarget"] + if target_id in env.domaindata["std"].get("typer", {}): + docname, labelid, sectionname = env.domaindata["std"]["typer"][target_id] + refnode = make_refnode( + env.app.builder, + node["refdoc"], + docname, + labelid, + nodes.Text(sectionname.strip()), + target_id, + ) + return refnode + else: + lineno = node.line or getattr(node.parent, "line", 0) + error_message = env.get_doctree(node["refdoc"]).reporter.error( + f"Unresolved :typer: reference: '{target_id}' in document '{node['refdoc']}'. " + f"Expected one of: {pformat(list(env.domaindata["std"]["typer"].keys()), indent=2)}", + line=lineno, + ) + msgid = node.document.set_id(error_message, node.parent) + problematic = nodes.problematic(node.rawsource, node.rawsource, refid=msgid) + prbid = node.document.set_id(problematic) + error_message.add_backref(prbid) + return problematic + + def typer_ref_role(name, rawtext, text, lineno, inliner, options={}, content=[]): env = inliner.document.settings.env target_id = nodes.make_id(text) @@ -960,19 +991,32 @@ def typer_ref_role(name, rawtext, text, lineno, inliner, options={}, content=[]) env.docname, docname, labelid, - nodes.Text(sectionname), + nodes.Text(sectionname.strip()), target_id, ) return [refnode], [] else: - msg = inliner.reporter.error(f'Unknown typer reference: "{text}"', line=lineno) - return [inliner.problematic(rawtext, rawtext, msg)], [msg] + pending = pending_xref( + rawtext, + refdomain="std", + reftype="typer", + reftarget=target_id, + modname=None, + classname=None, + refexplicit=True, + refwarn=True, + reftitle=text, + refdoc=env.docname, + ) + pending += nodes.Text(text) + return [pending], [] def setup(app: application.Sphinx) -> t.Dict[str, t.Any]: # Need autodoc to support mocking modules app.add_directive("typer", TyperDirective) app.add_role("typer", typer_ref_role) + app.connect("missing-reference", resolve_typer_reference) app.add_config_value( "typer_render_html", "sphinxcontrib.typer.typer_render_html", "env" diff --git a/tests/tests.py b/tests/tests.py index 4e75368..4d297e6 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -238,8 +238,8 @@ def check_svg(html, help_txt, svg_number=0, threshold=0.75): soup = bs(html, "html.parser") svg = soup.find_all("svg")[svg_number] assert svg is not None - txt = svg.text.strip() - assert similarity(svg.text.strip(), help_txt) > threshold + txt = svg.text.strip().replace("\xa0", " ") + assert similarity(txt, help_txt) > threshold return txt @@ -616,6 +616,27 @@ def check_refs(section, local): shutil.rmtree(bld_dir.parent) +def test_typer_ex_reference(): + clear_callbacks() + + html_dir, index_html = build_example( + "reference", "html", example_dir=TYPER_EXAMPLES + ) + + doc_help = check_svg( + (html_dir / "reference.html").read_text(), + get_typer_ex_help("reference", command_file="cli-ref"), + 0, + threshold=0.82, + ) + assert "python -m cli-ref.py" in doc_help + + index = bs(index_html, "html.parser") + for ref in index.find_all("section")[0].find_all("p")[0].find_all("a"): + assert ref.text == "python -m cli-ref.py" + assert ref.attrs["href"] == "reference.html#python-m-cli-ref-py" + + def test_typer_ex_composite(): EX_DIR = TYPER_EXAMPLES / "composite/composite" cli_py = EX_DIR / "cli.py" @@ -706,6 +727,16 @@ def test_build(first=False): continue assert t5 > t4, f"file {files[idx]} not regenerated." + # check navbar + navitems = list( + bs(index_html.read_text()).find("div", class_="sphinxsidebar").find_all("a") + ) + assert navitems[1].text == "composite" + assert navitems[2].text.strip() == "python -m cli.py repeat" + assert navitems[3].text == "cli subgroup" + assert navitems[4].text == "cli subgroup echo" + assert navitems[5].text == "cli subgroup multiply" + finally: os.system(f"git checkout {cli_py}") os.system(f"git checkout {group_py}") diff --git a/tests/typer/composite/repeat.rst b/tests/typer/composite/repeat.rst index b629f4c..6bff6cf 100644 --- a/tests/typer/composite/repeat.rst +++ b/tests/typer/composite/repeat.rst @@ -1,4 +1,5 @@ .. typer:: composite.cli.app:repeat + :prog: python -m cli.py repeat :width: 65 :convert-png: latex :make-sections: diff --git a/tests/typer/reference/cli-ref.py b/tests/typer/reference/cli-ref.py new file mode 100644 index 0000000..1403994 --- /dev/null +++ b/tests/typer/reference/cli-ref.py @@ -0,0 +1,13 @@ +import typer + +app = typer.Typer() + + +def reference(name: str): + typer.echo(name) + + +app.command(help="CLI ref tests.")(reference) + +if __name__ == "__main__": + app() diff --git a/tests/typer/reference/index.rst b/tests/typer/reference/index.rst new file mode 100644 index 0000000..c86f8e2 --- /dev/null +++ b/tests/typer/reference/index.rst @@ -0,0 +1,11 @@ +Reference Tests +--------------- + +This tests that references to commands like :typer:`python -m cli-ref.py` work. You can also use +a section id style reference: :typer:`python-m-cli-ref-py`. + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + reference diff --git a/tests/typer/reference/reference.rst b/tests/typer/reference/reference.rst new file mode 100644 index 0000000..e86e041 --- /dev/null +++ b/tests/typer/reference/reference.rst @@ -0,0 +1,8 @@ +Reference +========= + +.. typer:: cli-ref.app + :prog: python -m cli-ref.py + :width: 65 + :convert-png: latex + :make-sections: