From 25a6391fdf4456592c9c817bd42417750e202610 Mon Sep 17 00:00:00 2001 From: Benjy Weinberger Date: Mon, 13 Jul 2020 12:16:09 -0700 Subject: [PATCH 1/8] Have help-all dump a JSON blob containing all help info. [ci skip-rust-tests] --- src/python/pants/help/help_printer.py | 46 +++++++++++++++++-------- src/python/pants/option/arg_splitter.py | 23 +++++++------ 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/python/pants/help/help_printer.py b/src/python/pants/help/help_printer.py index 717323c8dff..f6154cf15c4 100644 --- a/src/python/pants/help/help_printer.py +++ b/src/python/pants/help/help_printer.py @@ -1,8 +1,10 @@ # Copyright 2015 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). - +import dataclasses +import json import sys import textwrap +from enum import Enum from typing import Dict, cast from colors import cyan, green @@ -12,6 +14,7 @@ from pants.help.help_formatter import HelpFormatter from pants.help.help_info_extracter import AllHelpInfo from pants.option.arg_splitter import ( + AllHelp, GoalsHelp, HelpRequest, NoGoalHelp, @@ -42,10 +45,12 @@ def print_hint() -> None: if isinstance(self._help_request, VersionHelp): print(pants_version()) - elif isinstance(self._help_request, OptionsHelp): - self._print_options_help() elif isinstance(self._help_request, GoalsHelp): self._print_goals_help() + elif isinstance(self._help_request, AllHelp): + self._print_all_help() + elif isinstance(self._help_request, OptionsHelp): + self._print_options_help() elif isinstance(self._help_request, UnknownGoalHelp): print("Unknown goals: {}".format(", ".join(self._help_request.unknown_goals))) print_hint() @@ -95,6 +100,9 @@ def format_goal(name: str, descr: str) -> str: ] print("\n".join(lines)) + def _print_all_help(self) -> None: + print(self._get_help_json()) + def _print_options_help(self) -> None: """Print a help screen. @@ -102,15 +110,9 @@ def _print_options_help(self) -> None: Note: Ony useful if called after options have been registered. """ - help_request = cast(OptionsHelp, self._help_request) - - if help_request.all_scopes: - help_scopes = set(self._all_help_info.scope_to_help_info.keys()) - else: - # The scopes explicitly mentioned by the user on the cmd line. - help_scopes = set(help_request.scopes) - + # The scopes explicitly mentioned by the user on the cmd line. + help_scopes = set(help_request.scopes) if help_scopes: for scope in sorted(help_scopes): help_str = self._format_help(scope, help_request.advanced) @@ -126,7 +128,7 @@ def _print_global_help(self, advanced: bool): print( f" {self._bin_name} [option ...] [goal ...] [target/file ...] Attempt the specified goals." ) - print(f" {self._bin_name} help Get help.") + print(f" {self._bin_name} help Get global help.") print( f" {self._bin_name} help [goal/subsystem] Get help for a goal or subsystem." ) @@ -137,7 +139,7 @@ def _print_global_help(self, advanced: bool): f" {self._bin_name} help-advanced [goal/subsystem] Get help for a goal's or subsystem's advanced options." ) print( - f" {self._bin_name} help-all Get help for all goals and subsystems." + f" {self._bin_name} help-all Get a JSON object containing all help info." ) print( f" {self._bin_name} goals List all installed goals." @@ -154,7 +156,7 @@ def _print_global_help(self, advanced: bool): print(self._format_help(GLOBAL_SCOPE, advanced)) def _format_help(self, scope: str, show_advanced_and_deprecated: bool) -> str: - """Return a help message for the options registered on this object. + """Return a human-readable help message for the options registered on this object. Assumes that self._help_request is an instance of OptionsHelp. """ @@ -177,3 +179,19 @@ def _format_help(self, scope: str, show_advanced_and_deprecated: bool) -> str: formatted_lines.append(f"{related_subsystems_label} {', '.join(related_scopes)}") formatted_lines.append("") return "\n".join(formatted_lines) + + class Encoder(json.JSONEncoder): + def default(self, o): + if callable(o): + return o.__name__ + if isinstance(o, type): + return type.__name__ + if isinstance(o, Enum): + return o.value + return super().default(o) + + def _get_help_json(self) -> str: + """Return a JSON object containing all the help info we have, for every scope.""" + return json.dumps( + dataclasses.asdict(self._all_help_info), sort_keys=True, indent=2, cls=self.Encoder + ) diff --git a/src/python/pants/option/arg_splitter.py b/src/python/pants/option/arg_splitter.py index b65c269683a..116cf15ad02 100644 --- a/src/python/pants/option/arg_splitter.py +++ b/src/python/pants/option/arg_splitter.py @@ -36,16 +36,19 @@ class OptionsHelp(HelpRequest): """The user requested help on which options they can set.""" advanced: bool = False - all_scopes: bool = False scopes: Tuple[str, ...] = () +class VersionHelp(HelpRequest): + """The user asked for the version of this instance of pants.""" + + class GoalsHelp(HelpRequest): """The user requested help for installed Goals.""" -class VersionHelp(HelpRequest): - """The user asked for the version of this instance of pants.""" +class AllHelp(HelpRequest): + """The user requested a dump of all help info.""" @dataclass(frozen=True) @@ -74,15 +77,15 @@ class ArgSplitter: _HELP_BASIC_ARGS = ("-h", "--help", "help") _HELP_ADVANCED_ARGS = ("--help-advanced", "help-advanced") - _HELP_ALL_SCOPES_ARGS = ("--help-all", "help-all") - _HELP_VERSION_ARGS = ("-v", "-V", "--version") + _HELP_VERSION_ARGS = ("-v", "-V", "--version", "version") _HELP_GOALS_ARGS = ("goals",) + _HELP_ALL_SCOPES_ARGS = ("help-all",) _HELP_ARGS = ( *_HELP_BASIC_ARGS, *_HELP_ADVANCED_ARGS, - *_HELP_ALL_SCOPES_ARGS, *_HELP_VERSION_ARGS, *_HELP_GOALS_ARGS, + *_HELP_ALL_SCOPES_ARGS, ) def __init__(self, known_scope_infos: Iterable[ScopeInfo]) -> None: @@ -91,6 +94,7 @@ def __init__(self, known_scope_infos: Iterable[ScopeInfo]) -> None: # that we heuristically identify target specs based on it containing /, : or being # a top-level directory. self._known_scopes = {si.scope for si in known_scope_infos} | { + "version", "goals", "help", "help-advanced", @@ -130,6 +134,8 @@ def _check_for_help_request(self, arg: str) -> bool: self._help_request = VersionHelp() elif arg in self._HELP_GOALS_ARGS: self._help_request = GoalsHelp() + elif arg in self._HELP_ALL_SCOPES_ARGS: + self._help_request = AllHelp() else: # First ensure that we have a basic OptionsHelp. if not self._help_request: @@ -137,10 +143,7 @@ def _check_for_help_request(self, arg: str) -> bool: # Now see if we need to enhance it. if isinstance(self._help_request, OptionsHelp): advanced = self._help_request.advanced or arg in self._HELP_ADVANCED_ARGS - all_scopes = self._help_request.all_scopes or arg in self._HELP_ALL_SCOPES_ARGS - self._help_request = dataclasses.replace( - self._help_request, advanced=advanced, all_scopes=all_scopes - ) + self._help_request = dataclasses.replace(self._help_request, advanced=advanced) return True def split_args(self, args: Sequence[str]) -> SplitArgs: From 9544feec656efd2314509d7923770d009d89c482 Mon Sep 17 00:00:00 2001 From: Benjy Weinberger Date: Mon, 13 Jul 2020 14:23:56 -0700 Subject: [PATCH 2/8] Lint # Rust tests will be skipped. Delete if not intended. [ci skip-rust-tests] --- src/python/pants/help/help_printer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/python/pants/help/help_printer.py b/src/python/pants/help/help_printer.py index f6154cf15c4..25cbc8fc42c 100644 --- a/src/python/pants/help/help_printer.py +++ b/src/python/pants/help/help_printer.py @@ -1,5 +1,6 @@ # Copyright 2015 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). + import dataclasses import json import sys From e94b052d04dee0e6ccb4dc4b08e07702caa1bfd1 Mon Sep 17 00:00:00 2001 From: Benjy Weinberger Date: Mon, 13 Jul 2020 14:35:40 -0700 Subject: [PATCH 3/8] Fix tests # Rust tests will be skipped. Delete if not intended. [ci skip-rust-tests] --- src/python/pants/help/help_integration_test.py | 2 +- src/python/pants/option/arg_splitter_test.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/python/pants/help/help_integration_test.py b/src/python/pants/help/help_integration_test.py index 28477039955..f3333839554 100644 --- a/src/python/pants/help/help_integration_test.py +++ b/src/python/pants/help/help_integration_test.py @@ -34,7 +34,7 @@ def test_help_all(self): assert "--pytest-version" in pants_run.stdout_data def test_help_all_advanced(self): - command = ["--help-all", "--help-advanced"] + command = ["help-all", "--help-advanced"] pants_run = self.run_pants(command=command) self.assert_success(pants_run) # Spot check to see that scope headings are printed even for advanced options diff --git a/src/python/pants/option/arg_splitter_test.py b/src/python/pants/option/arg_splitter_test.py index 95f2c70b1d2..068e686cdf0 100644 --- a/src/python/pants/option/arg_splitter_test.py +++ b/src/python/pants/option/arg_splitter_test.py @@ -299,8 +299,6 @@ def test_help_detection(self) -> None: assert_help_no_arguments("./pants --help --help-advanced", expected_help_advanced=True) assert_help_no_arguments("./pants --help-advanced --help", expected_help_advanced=True) assert_help_no_arguments("./pants help-all", expected_help_all=True) - assert_help_no_arguments("./pants --help-all", expected_help_all=True) - assert_help_no_arguments("./pants --help --help-all", expected_help_all=True) assert_help_no_arguments( "./pants help-advanced --help-all", expected_help_advanced=True, expected_help_all=True, ) From a6609280cbd1566073749630821ed738653fa0cf Mon Sep 17 00:00:00 2001 From: Benjy Weinberger Date: Mon, 13 Jul 2020 18:55:25 -0700 Subject: [PATCH 4/8] Fix test and lint # Rust tests will be skipped. Delete if not intended. [ci skip-rust-tests] --- src/python/pants/option/arg_splitter_test.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/python/pants/option/arg_splitter_test.py b/src/python/pants/option/arg_splitter_test.py index 068e686cdf0..5d23224a9ad 100644 --- a/src/python/pants/option/arg_splitter_test.py +++ b/src/python/pants/option/arg_splitter_test.py @@ -13,7 +13,7 @@ OptionsHelp, UnknownGoalHelp, VersionHelp, -) + AllHelp) from pants.option.scope import ScopeInfo from pants.util.contextutil import pushd, temporary_dir @@ -56,9 +56,7 @@ def assert_valid_split( assert expected_help_advanced == ( isinstance(splitter.help_request, OptionsHelp) and splitter.help_request.advanced ) - assert expected_help_all == ( - isinstance(splitter.help_request, OptionsHelp) and splitter.help_request.all_scopes - ) + assert expected_help_all == isinstance(splitter.help_request, AllHelp) assert expected_unknown_scopes == split_args.unknown_scopes @staticmethod @@ -299,15 +297,6 @@ def test_help_detection(self) -> None: assert_help_no_arguments("./pants --help --help-advanced", expected_help_advanced=True) assert_help_no_arguments("./pants --help-advanced --help", expected_help_advanced=True) assert_help_no_arguments("./pants help-all", expected_help_all=True) - assert_help_no_arguments( - "./pants help-advanced --help-all", expected_help_advanced=True, expected_help_all=True, - ) - assert_help_no_arguments( - "./pants --help-all --help --help-advanced", - expected_help_advanced=True, - expected_help_all=True, - ) - assert_help( "./pants -f", expected_goals=[], From 716c4b6dcbc95f0e2044a304489592c98da95410 Mon Sep 17 00:00:00 2001 From: Benjy Weinberger Date: Mon, 13 Jul 2020 19:54:21 -0700 Subject: [PATCH 5/8] Lint # Rust tests will be skipped. Delete if not intended. [ci skip-rust-tests] --- src/python/pants/option/arg_splitter_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/python/pants/option/arg_splitter_test.py b/src/python/pants/option/arg_splitter_test.py index 5d23224a9ad..1591d0944e4 100644 --- a/src/python/pants/option/arg_splitter_test.py +++ b/src/python/pants/option/arg_splitter_test.py @@ -8,12 +8,13 @@ from typing import Dict, List, Optional from pants.option.arg_splitter import ( + AllHelp, ArgSplitter, NoGoalHelp, OptionsHelp, UnknownGoalHelp, VersionHelp, - AllHelp) +) from pants.option.scope import ScopeInfo from pants.util.contextutil import pushd, temporary_dir From c8770a83ebc75711d8e12b17a9401d190f86a2c2 Mon Sep 17 00:00:00 2001 From: Benjy Weinberger Date: Tue, 14 Jul 2020 08:54:44 -0700 Subject: [PATCH 6/8] Fix tests # Rust tests will be skipped. Delete if not intended. [ci skip-rust-tests] --- .../pants/help/help_integration_test.py | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/python/pants/help/help_integration_test.py b/src/python/pants/help/help_integration_test.py index f3333839554..96e51bb14df 100644 --- a/src/python/pants/help/help_integration_test.py +++ b/src/python/pants/help/help_integration_test.py @@ -1,5 +1,6 @@ # Copyright 2015 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). +import json from pants.testutil.pants_run_integration_test import PantsRunIntegrationTest @@ -26,23 +27,14 @@ def test_help_all(self): command = ["help-all"] pants_run = self.run_pants(command=command) self.assert_success(pants_run) - # Spot check to see that scope headings are printed - assert "`pytest` subsystem options" in pants_run.stdout_data - # Spot check to see that full args for all options are printed - assert "--[no-]test-debug" in pants_run.stdout_data - # Spot check to see that subsystem options are printing - assert "--pytest-version" in pants_run.stdout_data - - def test_help_all_advanced(self): - command = ["help-all", "--help-advanced"] - pants_run = self.run_pants(command=command) - self.assert_success(pants_run) - # Spot check to see that scope headings are printed even for advanced options - assert "`pytest` subsystem options" in pants_run.stdout_data - assert "`pytest` subsystem advanced options" in pants_run.stdout_data - # Spot check to see that full args for all options are printed - assert "--[no-]test-debug" in pants_run.stdout_data - # Spot check to see that subsystem options are printing - assert "--pytest-version" in pants_run.stdout_data - # Spot check to see that advanced subsystem options are printing - assert "--pytest-timeout-default" in pants_run.stdout_data + all_help = json.loads(pants_run.stdout_data) + + # Spot check the data. + assert "name_to_goal_info" in all_help + assert "test" in all_help["name_to_goal_info"] + + assert "scope_to_help_info" in all_help + assert "" in all_help["scope_to_help_info"] + assert "pytest" in all_help["scope_to_help_info"] + assert len(all_help["scope_to_help_info"]["pytest"]["basic"]) > 0 + From 7f1350d6d854e56f6dc32cd2ad769174124aa549 Mon Sep 17 00:00:00 2001 From: Benjy Weinberger Date: Tue, 14 Jul 2020 10:27:14 -0700 Subject: [PATCH 7/8] Lint # Rust tests will be skipped. Delete if not intended. [ci skip-rust-tests] --- src/python/pants/help/help_integration_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/python/pants/help/help_integration_test.py b/src/python/pants/help/help_integration_test.py index 96e51bb14df..038b2d25ed4 100644 --- a/src/python/pants/help/help_integration_test.py +++ b/src/python/pants/help/help_integration_test.py @@ -37,4 +37,3 @@ def test_help_all(self): assert "" in all_help["scope_to_help_info"] assert "pytest" in all_help["scope_to_help_info"] assert len(all_help["scope_to_help_info"]["pytest"]["basic"]) > 0 - From 3768ce2c07fb8b972ad52a020522e7a323a53b86 Mon Sep 17 00:00:00 2001 From: Benjy Weinberger Date: Tue, 14 Jul 2020 12:03:19 -0700 Subject: [PATCH 8/8] lint # Rust tests will be skipped. Delete if not intended. [ci skip-rust-tests] --- src/python/pants/help/help_integration_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/python/pants/help/help_integration_test.py b/src/python/pants/help/help_integration_test.py index 038b2d25ed4..76b5c7dcc0f 100644 --- a/src/python/pants/help/help_integration_test.py +++ b/src/python/pants/help/help_integration_test.py @@ -1,5 +1,6 @@ # Copyright 2015 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). + import json from pants.testutil.pants_run_integration_test import PantsRunIntegrationTest