Skip to content

Commit 71840cc

Browse files
committed
fix #39, fix #40
1 parent 4a5ad55 commit 71840cc

File tree

8 files changed

+123
-9
lines changed

8 files changed

+123
-9
lines changed

doc/source/changelog.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
Change Log
33
==========
44

5+
v0.4.1 (21-AUG-2024)
6+
====================
7+
8+
* Fixed `:typer: role does not work if processed before the definition. <https://github.com/sphinx-contrib/typer/issues/40>`_
9+
* Fixed `:typer: role link text does not reflect the actual command invocation. <https://github.com/sphinx-contrib/typer/issues/39>`_
10+
511
v0.4.0 (19-AUG-2024)
612
====================
713

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "sphinxcontrib-typer"
3-
version = "0.4.0"
3+
version = "0.4.1"
44
description = "Auto generate docs for typer commands."
55
authors = ["Brian Kohan <[email protected]>"]
66
license = "MIT"

sphinxcontrib/typer/__init__.py

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from importlib import import_module
3434
from importlib.util import find_spec
3535
from pathlib import Path
36+
from pprint import pformat
3637

3738
import click
3839
from docutils import nodes
@@ -42,6 +43,7 @@
4243
from rich.console import Console
4344
from rich.theme import Theme
4445
from sphinx import application
46+
from sphinx.addnodes import pending_xref
4547
from sphinx.util import logging
4648
from sphinx.util.nodes import make_refnode
4749

@@ -52,7 +54,7 @@
5254
from typer.models import Context as TyperContext
5355
from typer.models import TyperInfo
5456

55-
VERSION = (0, 4, 0)
57+
VERSION = (0, 4, 1)
5658

5759
__title__ = "SphinxContrib Typer"
5860
__version__ = ".".join(str(i) for i in VERSION)
@@ -458,7 +460,7 @@ def generate_nodes(
458460
self.env.domaindata["std"].setdefault("typer", {})[section_id] = (
459461
self.env.docname,
460462
section_id,
461-
" ".join(section_id.split("-")),
463+
normal_cmd,
462464
)
463465

464466
# Summary
@@ -584,7 +586,7 @@ def run(self) -> t.Iterable[nodes.section]:
584586

585587
self.make_sections = "make-sections" in self.options
586588
self.nested = "show-nested" in self.options
587-
self.prog_name = self.options.get("prog", None)
589+
self.prog_name = self.options.get("prog", "")
588590
if "markup-mode" in self.options:
589591
self.markup_mode = self.options["markup-mode"]
590592

@@ -600,6 +602,8 @@ def run(self) -> t.Iterable[nodes.section]:
600602
"Unable to determine program name, please specify using " ":prog:"
601603
) from err
602604

605+
self.prog_name = self.prog_name.strip()
606+
603607
self.width = self.options.get("width", 65)
604608
self.iframe_height = self.options.get("iframe-height", None)
605609

@@ -950,6 +954,33 @@ def typer_convert_png(
950954
im.save(str(png_path)) # Saves the screenshot
951955

952956

957+
def resolve_typer_reference(app, env, node, contnode):
958+
target_id = node["reftarget"]
959+
if target_id in env.domaindata["std"].get("typer", {}):
960+
docname, labelid, sectionname = env.domaindata["std"]["typer"][target_id]
961+
refnode = make_refnode(
962+
env.app.builder,
963+
node["refdoc"],
964+
docname,
965+
labelid,
966+
nodes.Text(sectionname.strip()),
967+
target_id,
968+
)
969+
return refnode
970+
else:
971+
lineno = node.line or getattr(node.parent, "line", 0)
972+
error_message = env.get_doctree(node["refdoc"]).reporter.error(
973+
f"Unresolved :typer: reference: '{target_id}' in document '{node['refdoc']}'. "
974+
f"Expected one of: {pformat(list(env.domaindata["std"]["typer"].keys()), indent=2)}",
975+
line=lineno,
976+
)
977+
msgid = node.document.set_id(error_message, node.parent)
978+
problematic = nodes.problematic(node.rawsource, node.rawsource, refid=msgid)
979+
prbid = node.document.set_id(problematic)
980+
error_message.add_backref(prbid)
981+
return problematic
982+
983+
953984
def typer_ref_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
954985
env = inliner.document.settings.env
955986
target_id = nodes.make_id(text)
@@ -960,19 +991,32 @@ def typer_ref_role(name, rawtext, text, lineno, inliner, options={}, content=[])
960991
env.docname,
961992
docname,
962993
labelid,
963-
nodes.Text(sectionname),
994+
nodes.Text(sectionname.strip()),
964995
target_id,
965996
)
966997
return [refnode], []
967998
else:
968-
msg = inliner.reporter.error(f'Unknown typer reference: "{text}"', line=lineno)
969-
return [inliner.problematic(rawtext, rawtext, msg)], [msg]
999+
pending = pending_xref(
1000+
rawtext,
1001+
refdomain="std",
1002+
reftype="typer",
1003+
reftarget=target_id,
1004+
modname=None,
1005+
classname=None,
1006+
refexplicit=True,
1007+
refwarn=True,
1008+
reftitle=text,
1009+
refdoc=env.docname,
1010+
)
1011+
pending += nodes.Text(text)
1012+
return [pending], []
9701013

9711014

9721015
def setup(app: application.Sphinx) -> t.Dict[str, t.Any]:
9731016
# Need autodoc to support mocking modules
9741017
app.add_directive("typer", TyperDirective)
9751018
app.add_role("typer", typer_ref_role)
1019+
app.connect("missing-reference", resolve_typer_reference)
9761020

9771021
app.add_config_value(
9781022
"typer_render_html", "sphinxcontrib.typer.typer_render_html", "env"

tests/tests.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,8 @@ def check_svg(html, help_txt, svg_number=0, threshold=0.75):
238238
soup = bs(html, "html.parser")
239239
svg = soup.find_all("svg")[svg_number]
240240
assert svg is not None
241-
txt = svg.text.strip()
242-
assert similarity(svg.text.strip(), help_txt) > threshold
241+
txt = svg.text.strip().replace("\xa0", " ")
242+
assert similarity(txt, help_txt) > threshold
243243
return txt
244244

245245

@@ -616,6 +616,27 @@ def check_refs(section, local):
616616
shutil.rmtree(bld_dir.parent)
617617

618618

619+
def test_typer_ex_reference():
620+
clear_callbacks()
621+
622+
html_dir, index_html = build_example(
623+
"reference", "html", example_dir=TYPER_EXAMPLES
624+
)
625+
626+
doc_help = check_svg(
627+
(html_dir / "reference.html").read_text(),
628+
get_typer_ex_help("reference", command_file="cli-ref"),
629+
0,
630+
threshold=0.82,
631+
)
632+
assert "python -m cli-ref.py" in doc_help
633+
634+
index = bs(index_html, "html.parser")
635+
for ref in index.find_all("section")[0].find_all("p")[0].find_all("a"):
636+
assert ref.text == "python -m cli-ref.py"
637+
assert ref.attrs["href"] == "reference.html#python-m-cli-ref-py"
638+
639+
619640
def test_typer_ex_composite():
620641
EX_DIR = TYPER_EXAMPLES / "composite/composite"
621642
cli_py = EX_DIR / "cli.py"
@@ -706,6 +727,16 @@ def test_build(first=False):
706727
continue
707728
assert t5 > t4, f"file {files[idx]} not regenerated."
708729

730+
# check navbar
731+
navitems = list(
732+
bs(index_html.read_text()).find("div", class_="sphinxsidebar").find_all("a")
733+
)
734+
assert navitems[1].text == "composite"
735+
assert navitems[2].text.strip() == "python -m cli.py repeat"
736+
assert navitems[3].text == "cli subgroup"
737+
assert navitems[4].text == "cli subgroup echo"
738+
assert navitems[5].text == "cli subgroup multiply"
739+
709740
finally:
710741
os.system(f"git checkout {cli_py}")
711742
os.system(f"git checkout {group_py}")

tests/typer/composite/repeat.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.. typer:: composite.cli.app:repeat
2+
:prog: python -m cli.py repeat
23
:width: 65
34
:convert-png: latex
45
:make-sections:

tests/typer/reference/cli-ref.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import typer
2+
3+
app = typer.Typer()
4+
5+
6+
def reference(name: str):
7+
typer.echo(name)
8+
9+
10+
app.command(help="CLI ref tests.")(reference)
11+
12+
if __name__ == "__main__":
13+
app()

tests/typer/reference/index.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Reference Tests
2+
---------------
3+
4+
This tests that references to commands like :typer:`python -m cli-ref.py` work. You can also use
5+
a section id style reference: :typer:`python-m-cli-ref-py`.
6+
7+
.. toctree::
8+
:maxdepth: 1
9+
:caption: Contents:
10+
11+
reference

tests/typer/reference/reference.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Reference
2+
=========
3+
4+
.. typer:: cli-ref.app
5+
:prog: python -m cli-ref.py
6+
:width: 65
7+
:convert-png: latex
8+
:make-sections:

0 commit comments

Comments
 (0)