• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 19015773527

02 Nov 2025 05:33PM UTC coverage: 17.872% (-62.4%) from 80.3%
19015773527

Pull #22816

github

web-flow
Merge a12d75757 into 6c024e162
Pull Request #22816: Update Pants internal Python to 3.14

4 of 5 new or added lines in 3 files covered. (80.0%)

28452 existing lines in 683 files now uncovered.

9831 of 55007 relevant lines covered (17.87%)

0.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

15.29
/src/python/pants/help/help_formatter.py
1
# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
1✔
5

6
import json
1✔
7
import textwrap
1✔
8
from enum import Enum
1✔
9

10
from pants.help.help_info_extracter import OptionHelpInfo, OptionScopeHelpInfo, to_help_str
1✔
11
from pants.help.maybe_color import MaybeColor
1✔
12
from pants.option.ranked_value import Rank, RankedValue
1✔
13
from pants.util.docutil import bin_name, terminal_width
1✔
14
from pants.util.strutil import hard_wrap
1✔
15

16

17
class HelpFormatter(MaybeColor):
1✔
18
    def __init__(self, *, show_advanced: bool, show_deprecated: bool, color: bool) -> None:
1✔
UNCOV
19
        super().__init__(color=color)
×
UNCOV
20
        self._show_advanced = show_advanced
×
UNCOV
21
        self._show_deprecated = show_deprecated
×
UNCOV
22
        self._width = terminal_width()
×
23

24
    def format_options(self, oshi: OptionScopeHelpInfo) -> list[str]:
1✔
25
        """Return a help message for the specified options."""
UNCOV
26
        lines = []
×
27

UNCOV
28
        def add_option(ohis, *, category=None) -> None:
×
UNCOV
29
            lines.append("")
×
UNCOV
30
            goal_or_subsystem = "goal" if oshi.is_goal else "subsystem"
×
UNCOV
31
            display_scope = f"`{oshi.scope}` {goal_or_subsystem}" if oshi.scope else "Global"
×
UNCOV
32
            if category:
×
UNCOV
33
                title = f"{display_scope} {category} options"
×
UNCOV
34
                lines.append(self.maybe_green(f"{title}\n{'-' * len(title)}"))
×
35
            else:
36
                # The basic options section gets the description and options scope info.
37
                # No need to repeat those in the advanced section.
UNCOV
38
                title = f"{display_scope} options"
×
UNCOV
39
                lines.append(self.maybe_green(f"{title}\n{'-' * len(title)}\n"))
×
UNCOV
40
                lines.extend(hard_wrap(oshi.description, width=self._width))
×
UNCOV
41
                lines.append(" ")
×
UNCOV
42
                lines.append(f"Activated by {self.maybe_magenta(oshi.provider)}")
×
UNCOV
43
                config_section = f"[{oshi.scope or 'GLOBAL'}]"
×
UNCOV
44
                lines.append(f"Config section: {self.maybe_magenta(config_section)}")
×
UNCOV
45
            lines.append(" ")
×
UNCOV
46
            if not ohis:
×
UNCOV
47
                lines.append("None available.")
×
UNCOV
48
                return
×
UNCOV
49
            for ohi in ohis:
×
UNCOV
50
                lines.extend([*self.format_option(ohi), ""])
×
51

UNCOV
52
        add_option(oshi.basic)
×
UNCOV
53
        show_advanced = self._show_advanced or (not oshi.basic and oshi.advanced)
×
UNCOV
54
        if show_advanced:  # show advanced options if there are no basic ones.
×
UNCOV
55
            add_option(oshi.advanced, category="advanced")
×
UNCOV
56
        if self._show_deprecated and oshi.deprecated:
×
UNCOV
57
            add_option(oshi.deprecated, category="deprecated")
×
UNCOV
58
        if not show_advanced and oshi.advanced:
×
UNCOV
59
            lines.append(
×
60
                self.maybe_green(
61
                    f"Advanced options available. You can list them by running "
62
                    f"{bin_name()} help-advanced {oshi.scope}."
63
                )
64
            )
UNCOV
65
        return [*lines, ""]
×
66

67
    def format_option(self, ohi: OptionHelpInfo) -> list[str]:
1✔
68
        """Format the help output for a single option.
69

70
        :param ohi: Extracted information for option to print
71
        :return: Formatted help text for this option
72
        """
73

UNCOV
74
        def maybe_parens(s: str | None) -> str:
×
UNCOV
75
            return f" ({s})" if s else ""
×
76

UNCOV
77
        def format_value(ranked_val: RankedValue, prefix: str, left_padding: str) -> list[str]:
×
UNCOV
78
            if isinstance(ranked_val.value, (list, dict)):
×
79
                is_enum_list = (
×
80
                    isinstance(ranked_val.value, list)
81
                    and len(ranked_val.value) > 0
82
                    and isinstance(ranked_val.value[0], Enum)
83
                )
84
                normalized_val = (
×
85
                    [enum_elmt.value for enum_elmt in ranked_val.value]
86
                    if is_enum_list
87
                    else ranked_val.value
88
                )
89
                val_lines = json.dumps(normalized_val, sort_keys=True, indent=4).split("\n")
×
90
            else:
UNCOV
91
                val_lines = [to_help_str(ranked_val.value)]
×
UNCOV
92
            val_lines[0] = f"{prefix}{val_lines[0]}"
×
UNCOV
93
            val_lines[-1] = f"{val_lines[-1]}{maybe_parens(ranked_val.details)}"
×
UNCOV
94
            val_lines = [self.maybe_cyan(f"{left_padding}{line}") for line in val_lines]
×
UNCOV
95
            return val_lines
×
96

UNCOV
97
        def wrap(s: str) -> list[str]:
×
UNCOV
98
            return hard_wrap(s, indent=len(indent), width=self._width)
×
99

UNCOV
100
        indent = "      "
×
101

UNCOV
102
        arg_lines = [f"  {self.maybe_magenta(args)}" for args in ohi.display_args]
×
UNCOV
103
        arg_lines.append(self.maybe_magenta(f"  {ohi.env_var}"))
×
UNCOV
104
        arg_lines.append(self.maybe_magenta(f"  {ohi.config_key}"))
×
105

UNCOV
106
        choices = "" if ohi.choices is None else f"one of: [{', '.join(ohi.choices)}]"
×
UNCOV
107
        choices_lines = [
×
108
            f"{indent}{'  ' if i != 0 else ''}{self.maybe_cyan(s)}"
109
            for i, s in enumerate(textwrap.wrap(f"{choices}", self._width))
110
        ]
111

UNCOV
112
        deprecated_lines = []
×
UNCOV
113
        if ohi.deprecated_message:
×
UNCOV
114
            maybe_colorize = self.maybe_red if ohi.deprecation_active else self.maybe_yellow
×
UNCOV
115
            deprecated_lines.extend(wrap(maybe_colorize(ohi.deprecated_message)))
×
UNCOV
116
            if ohi.removal_hint:
×
117
                deprecated_lines.extend(wrap(maybe_colorize(ohi.removal_hint)))
×
118

UNCOV
119
        default_lines = format_value(RankedValue(Rank.HARDCODED, ohi.default), "default: ", indent)
×
UNCOV
120
        if not ohi.value_history:
×
121
            # Should never happen, but this keeps mypy happy.
122
            raise ValueError("No value history - options not parsed.")
×
123

UNCOV
124
        final_val = ohi.value_history.final_value
×
UNCOV
125
        curr_value_lines = format_value(final_val, "current value: ", indent)
×
126

UNCOV
127
        interesting_ranked_values = [
×
128
            rv
129
            for rv in reversed(ohi.value_history.ranked_values)
130
            if rv.rank not in (Rank.NONE, Rank.HARDCODED, final_val.rank)
131
        ]
UNCOV
132
        value_derivation_lines = [
×
133
            line
134
            for rv in interesting_ranked_values
135
            for line in format_value(rv, "overrode: ", f"{indent}    ")
136
        ]
UNCOV
137
        description_lines = wrap(ohi.help)
×
UNCOV
138
        if ohi.target_field_name:
×
139
            description_lines.extend(
×
140
                wrap(
141
                    f"\nCan be overriden by field `{ohi.target_field_name}` on "
142
                    "`local_environment`, `docker_environment`, or `remote_environment` targets."
143
                )
144
            )
UNCOV
145
        lines = [
×
146
            *arg_lines,
147
            *choices_lines,
148
            *default_lines,
149
            *curr_value_lines,
150
            *value_derivation_lines,
151
            *deprecated_lines,
152
            *description_lines,
153
        ]
UNCOV
154
        return lines
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc