From f3bc6a9f1ee0b0134460e24041461f6eb4c2a2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Prchl=C3=ADk?= Date: Thu, 2 Jan 2025 23:05:24 +0100 Subject: [PATCH 1/3] Add a trivial handler for :ref: role when rendering CLI help Sphinx takes care of it when building (HTML) docs, but for CLI help, we need to provide a handler of our own. But even a simple one is enough. --- tmt/utils/rest.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/tmt/utils/rest.py b/tmt/utils/rest.py index 6018b02f39..69d9ebcd61 100644 --- a/tmt/utils/rest.py +++ b/tmt/utils/rest.py @@ -6,12 +6,15 @@ """ import functools -from typing import Optional +from collections.abc import Mapping, Sequence +from typing import Any, Optional import click import docutils.frontend import docutils.nodes import docutils.parsers.rst +import docutils.parsers.rst.roles +import docutils.parsers.rst.states import docutils.utils import tmt.log @@ -259,9 +262,39 @@ def unknown_departure(self, node: docutils.nodes.Node) -> None: raise GeneralError(f"Unhandled ReST node '{node}'.") +# Role handling works out of the box when building docs with Sphinx, but +# for CLI rendering, we use docutils directly, and we need to provide +# handlers for roles supported by Sphinx. +# +# It might be possible to reuse Sphinx implementation, but a brief +# reading of Sphinx source gives an impression of pretty complex code. +# And we don't need anything fancy, how hard could it be, right? See +# https://docutils.sourceforge.io/docs/howto/rst-roles.html +def role_ref( + name: str, + rawtext: str, + text: str, + lineno: int, + inliner: docutils.parsers.rst.states.Inliner, + options: Mapping[str, Any], + content: Sequence[str]) \ + -> tuple[Sequence[docutils.nodes.reference], Sequence[docutils.nodes.reference]]: + """ + A handler for ``:ref:`` role. + + :returns: a simple :py:class:`docutils.nodes.Text` node with text of + the "link": ``foo`` for both ``:ref:`foo``` and + ``:ref:`foo```. + """ + + return ([docutils.nodes.reference(rawtext, text)], []) + + def parse_rst(text: str) -> docutils.nodes.document: """ Parse a ReST document into docutils tree of nodes """ + docutils.parsers.rst.roles.register_local_role('ref', role_ref) + parser = docutils.parsers.rst.Parser() components = (docutils.parsers.rst.Parser,) settings = docutils.frontend.OptionParser(components=components).get_default_values() From e7f4aa287a70c296c634a15d788680f109894f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Prchl=C3=ADk?= Date: Tue, 14 Jan 2025 15:55:11 +0100 Subject: [PATCH 2/3] squash: support newer docutils API --- tmt/utils/rest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tmt/utils/rest.py b/tmt/utils/rest.py index 69d9ebcd61..910b9001ba 100644 --- a/tmt/utils/rest.py +++ b/tmt/utils/rest.py @@ -276,8 +276,8 @@ def role_ref( text: str, lineno: int, inliner: docutils.parsers.rst.states.Inliner, - options: Mapping[str, Any], - content: Sequence[str]) \ + options: Optional[Mapping[str, Any]] = None, + content: Optional[Sequence[str]] = None) \ -> tuple[Sequence[docutils.nodes.reference], Sequence[docutils.nodes.reference]]: """ A handler for ``:ref:`` role. From 478fcb4a276788cb263ba392c9a6155868a384da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Prchl=C3=ADk?= Date: Fri, 24 Jan 2025 12:04:54 +0100 Subject: [PATCH 3/3] squash: disable plugin docstring rendering in sphinx --- tmt/steps/__init__.py | 3 ++- tmt/utils/rest.py | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/tmt/steps/__init__.py b/tmt/steps/__init__.py index 82812cc178..ffb66d0f26 100644 --- a/tmt/steps/__init__.py +++ b/tmt/steps/__init__.py @@ -1185,7 +1185,8 @@ def __init__( self.name = name self.class_ = class_ - self.doc = tmt.utils.rest.render_rst(doc, tmt.log.Logger.get_bootstrap_logger()) + self.doc = tmt.utils.rest.render_rst(doc, tmt.log.Logger.get_bootstrap_logger()) \ + if tmt.utils.rest.REST_RENDERING_ALLOWED else doc self.order = order # Parse summary and description from provided doc string diff --git a/tmt/utils/rest.py b/tmt/utils/rest.py index 910b9001ba..474404c2bf 100644 --- a/tmt/utils/rest.py +++ b/tmt/utils/rest.py @@ -6,6 +6,7 @@ """ import functools +import sys from collections.abc import Mapping, Sequence from typing import Any, Optional @@ -21,6 +22,16 @@ from tmt.log import Logger from tmt.utils import GeneralError +# We may be sharing parser structures with Sphinx, when it's generating +# docs. And that lead to problems, our roles conflicting with those +# registered by Sphinx, or parser calling Sphinx roles in our context. +# Both Sphinx and docutils rely on global mutable states, and +# monkeypatching it around our calls to parser does not work. To avoid +# issues, ReST renderign is disabled when we know our code runs under +# the control of Sphinx. +REST_RENDERING_ALLOWED = ('sphinx-build' not in sys.argv[0]) + + #: Special string representing a new-line in the stack of rendered #: paragraphs. NL = ''