Skip to content

Commit

Permalink
fix #39, fix #40
Browse files Browse the repository at this point in the history
  • Loading branch information
bckohan committed Aug 22, 2024
1 parent 4a5ad55 commit 71840cc
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 9 deletions.
6 changes: 6 additions & 0 deletions doc/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
Change Log
==========

v0.4.1 (21-AUG-2024)
====================

* Fixed `:typer: role does not work if processed before the definition. <https://github.com/sphinx-contrib/typer/issues/40>`_
* Fixed `:typer: role link text does not reflect the actual command invocation. <https://github.com/sphinx-contrib/typer/issues/39>`_

v0.4.0 (19-AUG-2024)
====================

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>"]
license = "MIT"
Expand Down
56 changes: 50 additions & 6 deletions sphinxcontrib/typer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"]

Expand All @@ -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)

Expand Down Expand Up @@ -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)
Expand All @@ -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"
Expand Down
35 changes: 33 additions & 2 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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}")
Expand Down
1 change: 1 addition & 0 deletions tests/typer/composite/repeat.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.. typer:: composite.cli.app:repeat
:prog: python -m cli.py repeat
:width: 65
:convert-png: latex
:make-sections:
Expand Down
13 changes: 13 additions & 0 deletions tests/typer/reference/cli-ref.py
Original file line number Diff line number Diff line change
@@ -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()
11 changes: 11 additions & 0 deletions tests/typer/reference/index.rst
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions tests/typer/reference/reference.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Reference
=========

.. typer:: cli-ref.app
:prog: python -m cli-ref.py
:width: 65
:convert-png: latex
:make-sections:

0 comments on commit 71840cc

Please sign in to comment.