Skip to content

Commit 8a13199

Browse files
committed
Render environment variables and actual CLI/fmf examples of plugin keys
Improves documentation by showin examples of keys with actual values. This should help readers of HTML docs, but eventually, when ReST rendering gets improved, it would end up in CLI help too.
1 parent 3336a03 commit 8a13199

File tree

5 files changed

+84
-9
lines changed

5 files changed

+84
-9
lines changed

docs/templates/plugins.rst.j2

+54-9
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,9 @@
22

33
{% macro render_field(plugin_id, plugin_data_class, field_name) %}
44
{% set _, option, _, _, metadata = container_field(plugin_data_class, field_name) %}
5+
{% set envvar = metadata.envvar or "TMT_PLUGIN_%s_%s_%s" | format(STEP.upper(), plugin_id.upper(), field_name.replace('-', '_').upper()) %}
56

6-
{% if metadata.metavar %}
7-
{{ option }}: ``{{ metadata.metavar }}``
8-
{% elif metadata.default is boolean %}
9-
{{ option }}: ``true|false``
10-
{% elif metadata.choices %}
11-
{{ option }}: ``{{ metadata.choices }}``
12-
{% else %}
13-
{{ option }}:
14-
{% endif %}
7+
{{ option }}
158
{% if metadata.help %}
169
{{ metadata.help | trim | indent(4, first=true) }}
1710
{% endif %}
@@ -41,6 +34,58 @@
4134
{{ raise_error(error_message) }}
4235
{% endif %}
4336
{% endif %}
37+
38+
Environment variable: ``{{ envvar }}``
39+
40+
In plan metadata:
41+
42+
.. code-block:: yaml
43+
44+
# how: {{ plugin_id }}
45+
# ...
46+
47+
{% if metadata.metavar %}
48+
{{ option }}: {{ metadata.metavar }}
49+
50+
{% for example in metadata.help_example_values %}
51+
{{ option }}: {{ example }}
52+
{% endfor %}
53+
{% elif metadata.default is boolean %}
54+
{{ option }}: true|false
55+
{% else %}
56+
{{ option }}:
57+
{% endif %}
58+
59+
On command-line:
60+
61+
.. code-block:: shell
62+
63+
# tmt run ... {{ STEP }} --how {{ plugin_id }} ...
64+
65+
{% if metadata.metavar %}
66+
{{ plugin_id }} --{{ option }} {{ metadata.metavar | shell_quote }} ...
67+
export {{ envvar }}={{ metadata.metavar | shell_quote }}
68+
69+
{% for example in metadata.help_example_values %}
70+
{{ plugin_id }} --{{ option }} {{ example | shell_quote }}
71+
{% endfor %}
72+
{% for example in metadata.help_example_values %}
73+
export {{ envvar }}={{ example | shell_quote }}
74+
{% endfor %}
75+
{% elif metadata.default is boolean %}
76+
{{ plugin_id }} --{{ option }}
77+
export {{ envvar }}=1|0
78+
{% else %}
79+
{{ plugin_id }} --{{ option }} ...
80+
export {{ envvar }}=...
81+
82+
{% for example in metadata.help_example_values %}
83+
{{ plugin_id }} --{{ option }} {{ example | shell_quote }}
84+
{% endfor %}
85+
{% for example in metadata.help_example_values %}
86+
export {{ envvar }}={{ example | shell_quote }}
87+
{% endfor %}
88+
{% endif %}
4489
{% endmacro %}
4590

4691
.. _/plugins/{{ STEP }}:

tmt/container/__init__.py

+12
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ class FieldMetadata(Generic[T]):
9696
#: Help text documenting the field.
9797
help: Optional[str] = None
9898

99+
#: Specific values that should be shown in the documentation as
100+
#: interesting examples of the field usage.
101+
help_example_values: list[str] = simple_field(default_factory=list)
102+
99103
#: If field accepts a value, this string would represent it in documentation.
100104
#: This stores the metavar provided when field was created - it may be unset.
101105
#: py:attr:`metavar` provides the actual metavar to be used.
@@ -666,6 +670,7 @@ def field(
666670
envvar: Optional[str] = None,
667671
deprecated: Optional['tmt.options.Deprecated'] = None,
668672
help: Optional[str] = None,
673+
help_example_values: Optional[list[str]] = None,
669674
show_default: bool = False,
670675
internal: bool = False,
671676
# Input data normalization - not needed, the field is a boolean
@@ -693,6 +698,7 @@ def field(
693698
envvar: Optional[str] = None,
694699
deprecated: Optional['tmt.options.Deprecated'] = None,
695700
help: Optional[str] = None,
701+
help_example_values: Optional[list[str]] = None,
696702
show_default: bool = False,
697703
internal: bool = False,
698704
# Input data normalization
@@ -719,6 +725,7 @@ def field(
719725
envvar: Optional[str] = None,
720726
deprecated: Optional['tmt.options.Deprecated'] = None,
721727
help: Optional[str] = None,
728+
help_example_values: Optional[list[str]] = None,
722729
show_default: bool = False,
723730
internal: bool = False,
724731
# Input data normalization
@@ -744,6 +751,7 @@ def field(
744751
envvar: Optional[str] = None,
745752
deprecated: Optional['tmt.options.Deprecated'] = None,
746753
help: Optional[str] = None,
754+
help_example_values: Optional[list[str]] = None,
747755
show_default: bool = False,
748756
internal: bool = False,
749757
# Input data normalization
@@ -770,6 +778,7 @@ def field(
770778
envvar: Optional[str] = None,
771779
deprecated: Optional['tmt.options.Deprecated'] = None,
772780
help: Optional[str] = None,
781+
help_example_values: Optional[list[str]] = None,
773782
show_default: bool = False,
774783
internal: bool = False,
775784
# Input data normalization
@@ -813,6 +822,8 @@ def field(
813822
:param help: the help string for the command-line option. Multiline strings
814823
can be used, :py:func:`textwrap.dedent` is applied before passing
815824
``help`` to :py:func:`click.option`.
825+
:param help_example_values: Specific values that should be shown in
826+
the documentation as interesting examples of the field usage.
816827
:param show_default: show default value
817828
Passed directly to :py:func:`click.option`.
818829
:param internal: if set, the field is treated as internal-only, and will not
@@ -854,6 +865,7 @@ def field(
854865
'tmt': FieldMetadata(
855866
internal=internal,
856867
help=textwrap.dedent(help).strip() if help else None,
868+
help_example_values=help_example_values or [],
857869
_metavar=metavar,
858870
default=default,
859871
default_factory=default_factory,

tmt/steps/prepare/ansible.py

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class PrepareAnsibleData(tmt.steps.prepare.PrepareStepData):
5353
option='--extra-args',
5454
metavar='ANSIBLE-PLAYBOOK-OPTIONS',
5555
help='Additional CLI options for ``ansible-playbook``.',
56+
help_example_values=['-vvv'],
5657
)
5758

5859
# ignore[override]: method violates a liskov substitution principle,

tmt/steps/report/reportportal.py

+1
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ class ReportReportPortalData(tmt.steps.report.ReportStepData):
220220
Size limit in bytes for traceback log upload to ReportPortal.
221221
The default limit is {DEFAULT_TRACEBACK_SIZE_LIMIT}.
222222
""",
223+
help_example_values=[str(DEFAULT_TRACEBACK_SIZE_LIMIT), '1MB'],
223224
normalize=tmt.utils.normalize_data_amount,
224225
serialize=lambda limit: str(limit),
225226
unserialize=lambda serialized: tmt.hardware.UNITS(serialized),

tmt/utils/templates.py

+16
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"""
77

88
import re
9+
import shlex
910
import textwrap
1011
from re import Match
1112
from typing import (
@@ -310,6 +311,21 @@ def _template_filter_web_git_url( # type: ignore[reportUnusedFunction,unused-ig
310311
return web_git_url(url, ref, path)
311312

312313

314+
def _template_filter_shell_quote( # type: ignore[reportUnusedFunction,unused-ignore]
315+
s: str,
316+
) -> str:
317+
"""
318+
Return a shell-escaped version of the string.
319+
320+
.. code-block:: jinja
321+
322+
# "foo bar" -> "'foo bar'"
323+
{{ "foo bar" | shell_quote }}
324+
"""
325+
326+
return shlex.quote(s)
327+
328+
313329
TEMPLATE_FILTERS: dict[str, Callable[..., Any]] = {
314330
_name.replace('_template_filter_', ''): _obj
315331
for _name, _obj in locals().items()

0 commit comments

Comments
 (0)