def run_goal( targets: Sequence[Target], *, target_type: Optional[List[str]] = None, address_regex: Optional[List[str]] = None, tag_regex: Optional[List[str]] = None, granularity: Optional[TargetGranularity] = None, ) -> str: console = MockConsole(use_colors=False) run_rule_with_mocks( filter_targets, rule_args=[ Targets(targets), create_goal_subsystem( FilterSubsystem, sep="\\n", output_file=None, target_type=target_type or [], address_regex=address_regex or [], tag_regex=tag_regex or [], granularity=granularity or TargetGranularity.all_targets, # Deprecated. type=[], target=[], regex=[], ancestor=[], ), console, RegisteredTargetTypes.create({type(tgt) for tgt in targets}), ], ) assert not console.stderr.getvalue() return cast(str, console.stdout.getvalue())
def run_goal( targets: Sequence[Target], *, target_type: Optional[List[str]] = None, address_regex: Optional[List[str]] = None, tag_regex: Optional[List[str]] = None, ) -> str: console = MockConsole(use_colors=False) run_rule( filter_targets, rule_args=[ Targets(targets), create_goal_subsystem( FilterOptions, sep="\\n", output_file=None, target_type=target_type or [], address_regex=address_regex or [], tag_regex=tag_regex or [], ), console, RegisteredTargetTypes.create({type(tgt) for tgt in targets}), ], ) assert not console.stderr.getvalue() return cast(str, console.stdout.getvalue())
def test_determine_renamed_fields() -> None: class DeprecatedField(StringField): alias = "new_name" deprecated_alias = "old_name" deprecated_alias_removal_version = "99.9.0.dev0" class OkayField(StringField): alias = "okay" class Tgt(Target): alias = "tgt" core_fields = (DeprecatedField, OkayField) deprecated_alias = "deprecated_tgt" deprecated_alias_removal_version = "99.9.0.dev0" class TgtGenerator(TargetGenerator): alias = "generator" core_fields = () moved_fields = (DeprecatedField, OkayField) registered_targets = RegisteredTargetTypes.create([Tgt, TgtGenerator]) result = determine_renamed_field_types(registered_targets, UnionMembership({})) deprecated_fields = FrozenDict( {DeprecatedField.deprecated_alias: DeprecatedField.alias}) assert result.target_field_renames == FrozenDict({ k: deprecated_fields for k in (TgtGenerator.alias, Tgt.alias, Tgt.deprecated_alias) })
def run_goal( targets: Sequence[Target], *, target_type: list[str] | None = None, address_regex: list[str] | None = None, tag_regex: list[str] | None = None, granularity: TargetGranularity | None = None, ) -> str: with mock_console(create_options_bootstrapper()) as (console, stdio_reader): run_rule_with_mocks( filter_targets, rule_args=[ Targets(targets), create_goal_subsystem( FilterSubsystem, sep="\\n", output_file=None, target_type=target_type or [], address_regex=address_regex or [], tag_regex=tag_regex or [], granularity=granularity or TargetGranularity.all_targets, # Deprecated. type=[], target=[], regex=[], ancestor=[], ), console, RegisteredTargetTypes.create({type(tgt) for tgt in targets}), ], ) assert not stdio_reader.get_stderr() return stdio_reader.get_stdout()
def __init__(self, options: Options): super().__init__(options.for_global_scope().colors) self._all_help_info = HelpInfoExtracter.get_all_help_info( options, # We only care about the options-related help info, so we pass in # dummy values for the other arguments. UnionMembership({}), lambda x: tuple(), RegisteredTargetTypes({}), )
def single_target_run( self, *, console: MockConsole, program_text: bytes, address_spec: str, ) -> Run: workspace = Workspace(self.scheduler) interactive_runner = InteractiveRunner(self.scheduler) class TestBinaryConfiguration(BinaryConfiguration): required_fields = () class TestBinaryTarget(Target): alias = "binary" core_fields = () address = Address.parse(address_spec) origin = SingleAddress(address.spec_path, address.target_name) res = run_rule( run, rule_args=[ console, workspace, interactive_runner, BuildRoot(), TargetsWithOrigins([ TargetWithOrigin( target=TestBinaryTarget(unhydrated_values={}, address=address), origin=origin, ) ]), create_goal_subsystem(RunOptions, args=[]), create_subsystem(GlobalOptions, pants_workdir=self.pants_workdir), UnionMembership(union_rules={ BinaryConfiguration: OrderedSet([TestBinaryConfiguration]) }), RegisteredTargetTypes.create([TestBinaryTarget]), ], mock_gets=[ MockGet( product_type=CreatedBinary, subject_type=TestBinaryConfiguration, mock=lambda _: self.create_mock_binary(program_text), ), ], ) return cast(Run, res)
def run_goal(*, union_membership: Optional[UnionMembership] = None, details_target: Optional[str] = None) -> str: console = MockConsole(use_colors=False) run_rule( list_target_types, rule_args=[ RegisteredTargetTypes.create( [FortranBinary, FortranLibrary, FortranTests]), union_membership or UnionMembership({}), MockOptions(details=details_target), console, ], ) return cast(str, console.stdout.getvalue())
def run( self, build_config: BuildConfiguration, graph_session: GraphSession, options: Options, specs: Specs, union_membership: UnionMembership, ) -> ExitCode: for server_request_type in union_membership.get(ExplorerServerRequest): logger.info(f"Using {server_request_type.__name__} to create the explorer server.") break else: logger.error( softwrap( """ There is no Explorer backend server implementation registered. Activate a backend/plugin that registers an implementation for the `ExplorerServerRequest` union to fix this issue. """ ) ) return 127 all_help_info = HelpInfoExtracter.get_all_help_info( options, union_membership, graph_session.goal_consumed_subsystem_scopes, RegisteredTargetTypes.create(build_config.target_types), build_config, ) request_state = RequestState( all_help_info=all_help_info, build_configuration=build_config, scheduler_session=graph_session.scheduler_session, ) server_request = server_request_type( address=self.address, port=self.port, request_state=request_state, ) server = request_state.product_request( ExplorerServer, (server_request,), poll=True, timeout=90, ) return server.run()
def _print_help(self, request: HelpRequest) -> ExitCode: global_options = self.options.for_global_scope() all_help_info = HelpInfoExtracter.get_all_help_info( self.options, self.union_membership, self.graph_session.goal_consumed_subsystem_scopes, RegisteredTargetTypes.create(self.build_config.target_types), ) help_printer = HelpPrinter( bin_name=global_options.pants_bin_name, help_request=request, all_help_info=all_help_info, color=global_options.colors, ) return help_printer.print_help()
def run_goal( *, union_membership: Optional[UnionMembership] = None, details_target: Optional[str] = None ) -> str: console = MockConsole(use_colors=False) run_rule_with_mocks( list_target_types, rule_args=[ RegisteredTargetTypes.create([FortranBinary, FortranLibrary, FortranTests]), union_membership or UnionMembership({}), create_goal_subsystem( TargetTypesSubsystem, sep="\\n", output_file=None, details=details_target ), console, PantsBin(name="./BNF"), ], ) return cast(str, console.stdout.getvalue())
def test_specs_filter() -> None: class MockTgt1(Target): alias = "tgt1" core_fields = (Tags, ) class MockTgt2(Target): alias = "tgt2" core_fields = (Tags, ) specs_filter = SpecsFilter.create( create_goal_subsystem( FilterSubsystem, target_type=["tgt1"], tag_regex=[], address_regex=[], granularity=TargetGranularity.all_targets, ), RegisteredTargetTypes({ "tgt1": MockTgt1, "tgt2": MockTgt2 }), tags=["-a", "+b"], exclude_target_regexps=["skip-me"], ) def make_tgt1(name: str, tags: list[str] | None = None) -> MockTgt1: return MockTgt1({Tags.alias: tags}, Address("", target_name=name)) def make_tgt2(name: str, tags: list[str] | None = None) -> MockTgt2: return MockTgt2({Tags.alias: tags}, Address("", target_name=name)) untagged_tgt = make_tgt1(name="untagged") b_tagged_tgt = make_tgt1(name="b-tagged", tags=["b"]) b_tagged_exclude_regex_tgt = make_tgt1(name="skip-me", tags=["b"]) a_and_b_tagged_tgt = make_tgt1(name="a-and-b-tagged", tags=["a", "b"]) # Even though this has the tag `b`, it should be excluded because the target type. tgt2 = make_tgt2("tgt2", tags=["b"]) def matches(tgt: Target) -> bool: return specs_filter.matches(tgt) assert matches(b_tagged_tgt) is True for t in (untagged_tgt, b_tagged_exclude_regex_tgt, a_and_b_tagged_tgt, tgt2): assert matches(t) is False
def run( self, build_config: BuildConfiguration, graph_session: GraphSession, options: Options, specs: Specs, union_membership: UnionMembership, ) -> ExitCode: all_help_info = HelpInfoExtracter.get_all_help_info( options, union_membership, graph_session.goal_consumed_subsystem_scopes, RegisteredTargetTypes.create(build_config.target_types), build_config, ) global_options = options.for_global_scope() help_printer = HelpPrinter( help_request=self.create_help_request(options), all_help_info=all_help_info, color=global_options.colors, ) return help_printer.print_help()
def setup_legacy_graph_extended( options_bootstrapper: OptionsBootstrapper, build_configuration: BuildConfiguration, execution_options: ExecutionOptions, *, pants_ignore_patterns: List[str], use_gitignore: bool, local_store_dir: str, local_execution_root_dir: str, named_caches_dir: str, build_root: Optional[str] = None, native: Optional[Native] = None, glob_match_error_behavior: GlobMatchErrorBehavior = GlobMatchErrorBehavior.warn, build_patterns: Optional[Iterable[str]] = None, build_file_prelude_globs: Optional[Iterable[str]] = None, build_ignore_patterns: Optional[Iterable[str]] = None, tags: Optional[Iterable[str]] = None, exclude_target_regexps: Optional[Iterable[str]] = None, subproject_roots: Optional[Iterable[str]] = None, include_trace_on_error: bool = True, ) -> LegacyGraphScheduler: """Construct and return the components necessary for LegacyBuildGraph construction.""" build_root = build_root or get_buildroot() build_configuration = build_configuration or BuildConfigInitializer.get( options_bootstrapper) bootstrap_options = options_bootstrapper.bootstrap_options.for_global_scope( ) execution_options = execution_options or DEFAULT_EXECUTION_OPTIONS build_file_aliases = build_configuration.registered_aliases rules = build_configuration.rules union_membership = UnionMembership.from_rules( build_configuration.union_rules) registered_target_types = RegisteredTargetTypes.create( build_configuration.target_types) parser = Parser(target_type_aliases=registered_target_types.aliases, object_aliases=build_file_aliases) address_mapper = AddressMapper( parser=parser, prelude_glob_patterns=build_file_prelude_globs, build_patterns=build_patterns, build_ignore_patterns=build_ignore_patterns, tags=tags, exclude_target_regexps=exclude_target_regexps, subproject_roots=subproject_roots, ) @rule def address_mapper_singleton() -> AddressMapper: return address_mapper @rule def glob_match_error_behavior_singleton() -> GlobMatchErrorBehavior: return glob_match_error_behavior @rule def build_configuration_singleton() -> BuildConfiguration: return build_configuration @rule def registered_target_types_singleton() -> RegisteredTargetTypes: return registered_target_types @rule def union_membership_singleton() -> UnionMembership: return union_membership @rule def build_root_singleton() -> BuildRoot: return cast(BuildRoot, BuildRoot.instance) # Create a Scheduler containing graph and filesystem rules, with no installed goals. rules = FrozenOrderedSet(( *collect_rules(locals()), RootRule(Console), *build_files.rules(), *fs.rules(), *graph.rules(), *uuid.rules(), *options_parsing.rules(), *process.rules(), *create_platform_rules(), *changed_rules(), *rules, )) goal_map = EngineInitializer._make_goal_map_from_rules(rules) def ensure_absolute_path(v: str) -> str: return Path(v).resolve().as_posix() scheduler = Scheduler( native=native, ignore_patterns=pants_ignore_patterns, use_gitignore=use_gitignore, build_root=build_root, local_store_dir=ensure_absolute_path(local_store_dir), local_execution_root_dir=ensure_absolute_path( local_execution_root_dir), named_caches_dir=ensure_absolute_path(named_caches_dir), rules=rules, union_membership=union_membership, execution_options=execution_options, include_trace_on_error=include_trace_on_error, visualize_to_dir=bootstrap_options.native_engine_visualize_to, ) return LegacyGraphScheduler(scheduler, build_file_aliases, goal_map)
def run_test_rule( self, *, config: Type[TestConfiguration], targets: List[TargetWithOrigin], debug: bool = False, include_sources: bool = True, ) -> Tuple[int, str]: console = MockConsole(use_colors=False) options = MockOptions(debug=debug, run_coverage=False) interactive_runner = InteractiveRunner(self.scheduler) workspace = Workspace(self.scheduler) union_membership = UnionMembership( {TestConfiguration: OrderedSet([config])}) def mock_coordinator_of_tests( wrapped_config: WrappedTestConfiguration, ) -> AddressAndTestResult: config = wrapped_config.config return AddressAndTestResult( address=config.address, test_result=config.test_result, # type: ignore[attr-defined] ) result: Test = run_rule( run_tests, rule_args=[ console, options, interactive_runner, TargetsWithOrigins(targets), workspace, union_membership, RegisteredTargetTypes.create([MockTarget]), ], mock_gets=[ MockGet( product_type=AddressAndTestResult, subject_type=WrappedTestConfiguration, mock=lambda wrapped_config: mock_coordinator_of_tests( wrapped_config), ), MockGet( product_type=TestDebugRequest, subject_type=TestConfiguration, mock=lambda _: TestDebugRequest(self.make_ipr()), ), MockGet( product_type=HydratedSources, subject_type=HydrateSourcesRequest, mock=lambda _: HydratedSources( Snapshot( directory_digest=EMPTY_DIRECTORY_DIGEST, files=cast(Tuple[str, ...], ("test.hs", ) if include_sources else ()), dirs=(), ), filespec={"globs": []}, ), ), MockGet( product_type=CoverageReport, subject_type=CoverageDataBatch, mock=lambda _: FilesystemCoverageReport( result_digest=EMPTY_DIRECTORY_DIGEST, directory_to_materialize_to=PurePath("mockety/mock"), report_file=None, ), ), ], union_membership=union_membership, ) return result.exit_code, console.stdout.getvalue()
def setup_legacy_graph_extended( pants_ignore_patterns: List[str], use_gitignore: bool, local_store_dir: str, local_execution_root_dir: str, named_caches_dir: str, build_file_prelude_globs: Tuple[str, ...], options_bootstrapper: OptionsBootstrapper, build_configuration: BuildConfiguration, execution_options: ExecutionOptions, build_root: Optional[str] = None, native: Optional[Native] = None, glob_match_error_behavior: GlobMatchErrorBehavior = GlobMatchErrorBehavior.warn, build_ignore_patterns=None, exclude_target_regexps=None, subproject_roots=None, include_trace_on_error: bool = True, ) -> LegacyGraphScheduler: """Construct and return the components necessary for LegacyBuildGraph construction. :param local_store_dir: The directory to use for storing the engine's LMDB store in. :param local_execution_root_dir: The directory to use for local execution sandboxes. :param named_caches_dir: The base directory for named cache storage. :param build_file_prelude_globs: Globs to match files to be prepended to all BUILD files. :param build_root: A path to be used as the build root. If None, then default is used. :param native: An instance of the native-engine subsystem. :param options_bootstrapper: A `OptionsBootstrapper` object containing bootstrap options. :param build_configuration: The `BuildConfiguration` object to get build file aliases from. :param glob_match_error_behavior: How to behave if a glob specified for a target's sources or bundles does not expand to anything. :param list build_ignore_patterns: A list of paths ignore patterns used when searching for BUILD files, usually taken from the '--build-ignore' global option. :param list exclude_target_regexps: A list of regular expressions for excluding targets. :param list subproject_roots: Paths that correspond with embedded build roots under the current build root. :param include_trace_on_error: If True, when an error occurs, the error message will include the graph trace. :param execution_options: Option values for (remote) process execution. """ build_root = build_root or get_buildroot() build_configuration = build_configuration or BuildConfigInitializer.get( options_bootstrapper) bootstrap_options = options_bootstrapper.bootstrap_options.for_global_scope( ) build_file_aliases = build_configuration.registered_aliases() rules = build_configuration.rules() registered_target_types = RegisteredTargetTypes.create( build_configuration.target_types()) symbol_table = _legacy_symbol_table(build_file_aliases, registered_target_types) execution_options = execution_options or DEFAULT_EXECUTION_OPTIONS # Register "literal" subjects required for these rules. parser = LegacyPythonCallbacksParser(symbol_table, build_file_aliases) address_mapper = AddressMapper( parser=parser, prelude_glob_patterns=build_file_prelude_globs, build_ignore_patterns=build_ignore_patterns, exclude_target_regexps=exclude_target_regexps, subproject_roots=subproject_roots, ) @rule def glob_match_error_behavior_singleton() -> GlobMatchErrorBehavior: return glob_match_error_behavior @rule def build_configuration_singleton() -> BuildConfiguration: return build_configuration @rule def symbol_table_singleton() -> SymbolTable: return symbol_table @rule def registered_target_types_singleton() -> RegisteredTargetTypes: return registered_target_types @rule def union_membership_singleton() -> UnionMembership: return UnionMembership(build_configuration.union_rules()) @rule def build_root_singleton() -> BuildRoot: return cast(BuildRoot, BuildRoot.instance) # Create a Scheduler containing graph and filesystem rules, with no installed goals. The # LegacyBuildGraph will explicitly request the products it needs. rules = ( RootRule(Console), glob_match_error_behavior_singleton, build_configuration_singleton, symbol_table_singleton, registered_target_types_singleton, union_membership_singleton, build_root_singleton, *interactive_runner.rules(), *graph.rules(), *options_parsing.rules(), *process.rules(), *target.rules(), *create_legacy_graph_tasks(), *create_fs_rules(), *create_platform_rules(), *create_graph_rules(address_mapper), *structs_rules(), *changed_rules(), *binary_tool_rules(), *binary_util_rules(), *rules, ) goal_map = EngineInitializer._make_goal_map_from_rules(rules) union_rules = build_configuration.union_rules() scheduler = Scheduler( native=native, ignore_patterns=pants_ignore_patterns, use_gitignore=use_gitignore, build_root=build_root, local_store_dir=local_store_dir, local_execution_root_dir=local_execution_root_dir, named_caches_dir=named_caches_dir, rules=rules, union_rules=union_rules, execution_options=execution_options, include_trace_on_error=include_trace_on_error, visualize_to_dir=bootstrap_options.native_engine_visualize_to, ) return LegacyGraphScheduler(scheduler, build_file_aliases, goal_map)
def setup_graph_extended( build_configuration: BuildConfiguration, execution_options: ExecutionOptions, *, executor: PyExecutor, pants_ignore_patterns: list[str], use_gitignore: bool, local_store_options: LocalStoreOptions, local_execution_root_dir: str, named_caches_dir: str, use_deprecated_python_macros: bool, ca_certs_path: str | None = None, build_root: str | None = None, include_trace_on_error: bool = True, engine_visualize_to: str | None = None, watch_filesystem: bool = True, ) -> GraphScheduler: build_root_path = build_root or get_buildroot() rules = build_configuration.rules union_membership: UnionMembership registered_target_types = RegisteredTargetTypes.create( build_configuration.target_types) execution_options = execution_options or DEFAULT_EXECUTION_OPTIONS @rule def parser_singleton() -> Parser: return Parser( build_root=build_root_path, target_type_aliases=registered_target_types.aliases, object_aliases=build_configuration.registered_aliases, use_deprecated_python_macros=use_deprecated_python_macros, ) @rule def build_configuration_singleton() -> BuildConfiguration: return build_configuration @rule def registered_target_types_singleton() -> RegisteredTargetTypes: return registered_target_types @rule def union_membership_singleton() -> UnionMembership: return union_membership @rule def build_root_singleton() -> BuildRoot: return cast(BuildRoot, BuildRoot.instance) # Create a Scheduler containing graph and filesystem rules, with no installed goals. rules = FrozenOrderedSet(( *collect_rules(locals()), *build_files.rules(), *fs.rules(), *environment.rules(), *desktop.rules(), *graph.rules(), *options_parsing.rules(), *process.rules(), *system_binaries.rules(), *platform.rules(), *changed_rules(), *streaming_workunit_handler_rules(), *specs_calculator.rules(), *rules, )) goal_map = EngineInitializer._make_goal_map_from_rules(rules) union_membership = UnionMembership.from_rules(( *build_configuration.union_rules, *(r for r in rules if isinstance(r, UnionRule)), )) rules = FrozenOrderedSet(( *rules, # Install queries for each Goal. *(QueryRule(goal_type, GraphSession.goal_param_types) for goal_type in goal_map.values()), # Install queries for each request/response pair used by the BSP support. # Note: These are necessary because the BSP support is a built-in goal that makes # synchronous requests into the engine. *(QueryRule(impl.response_type, (impl.request_type, Workspace)) for impl in union_membership.get(BSPHandlerMapping)), QueryRule(Snapshot, [PathGlobs]), # Used by the SchedulerService. )) def ensure_absolute_path(v: str) -> str: return Path(v).resolve().as_posix() def ensure_optional_absolute_path(v: str | None) -> str | None: if v is None: return None return ensure_absolute_path(v) scheduler = Scheduler( ignore_patterns=pants_ignore_patterns, use_gitignore=use_gitignore, build_root=build_root_path, local_execution_root_dir=ensure_absolute_path( local_execution_root_dir), named_caches_dir=ensure_absolute_path(named_caches_dir), ca_certs_path=ensure_optional_absolute_path(ca_certs_path), rules=rules, union_membership=union_membership, executor=executor, execution_options=execution_options, local_store_options=local_store_options, include_trace_on_error=include_trace_on_error, visualize_to_dir=engine_visualize_to, watch_filesystem=watch_filesystem, ) return GraphScheduler(scheduler, goal_map)
def test_get_all_help_info(): class Global(Subsystem): options_scope = GLOBAL_SCOPE help = "Global options." opt1 = IntOption("--opt1", default=42, help="Option 1") # This is special in having a short option `-l`. Make sure it works. level = LogLevelOption() class Foo(Subsystem): options_scope = "foo" help = "A foo." opt2 = BoolOption("--opt2", default=True, advanced=True, help="Option 2") class Bar(GoalSubsystem): name = "bar" help = "The bar goal." deprecated_options_scope = "bar-old" deprecated_options_scope_removal_version = "9.9.999" class QuxField(StringField): alias = "qux" default = "blahblah" help = "A qux string." class QuuxField(IntField): alias = "quux" required = True help = "A quux int.\n\nMust be non-zero. Or zero. Whatever you like really." class BazLibrary(Target): alias = "baz_library" help = "A library of baz-es.\n\nUse it however you like." core_fields = [QuxField, QuuxField] options = Options.create( env={}, config=Config.load([]), known_scope_infos=[ Global.get_scope_info(), Foo.get_scope_info(), Bar.get_scope_info() ], args=["./pants"], bootstrap_option_values=None, ) Global.register_options_on_scope(options) Foo.register_options_on_scope(options) Bar.register_options_on_scope(options) @rule def rule_info_test(foo: Foo) -> Target: """This rule is for testing info extraction only.""" def fake_consumed_scopes_mapper(scope: str) -> Tuple[str, ...]: return ("somescope", f"used_by_{scope or 'GLOBAL_SCOPE'}") bc_builder = BuildConfiguration.Builder() bc_builder.register_subsystems("help_info_extracter_test", (Foo, Bar)) bc_builder.register_target_types("help_info_extracter_test", (BazLibrary, )) bc_builder.register_rules("help_info_extracter_test", collect_rules(locals())) all_help_info = HelpInfoExtracter.get_all_help_info( options, UnionMembership({}), fake_consumed_scopes_mapper, RegisteredTargetTypes({BazLibrary.alias: BazLibrary}), bc_builder.create(), ) all_help_info_dict = all_help_info.asdict() expected_all_help_info_dict = { "scope_to_help_info": { GLOBAL_SCOPE: { "scope": GLOBAL_SCOPE, "description": "Global options.", "provider": "", "is_goal": False, "deprecated_scope": None, "basic": ( { "display_args": ("--opt1=<int>", ), "comma_separated_display_args": "--opt1=<int>", "scoped_cmd_line_args": ("--opt1", ), "unscoped_cmd_line_args": ("--opt1", ), "config_key": "opt1", "env_var": "PANTS_OPT1", "value_history": { "ranked_values": ( { "rank": Rank.NONE, "value": None, "details": None }, { "rank": Rank.HARDCODED, "value": 42, "details": None }, ), }, "typ": int, "default": 42, "help": "Option 1", "deprecation_active": False, "deprecated_message": None, "removal_version": None, "removal_hint": None, "choices": None, "comma_separated_choices": None, }, { "display_args": ("-l=<LogLevel>", "--level=<LogLevel>"), "comma_separated_display_args": "-l=<LogLevel>, --level=<LogLevel>", "scoped_cmd_line_args": ("-l", "--level"), "unscoped_cmd_line_args": ("-l", "--level"), "config_key": "level", "env_var": "PANTS_LEVEL", "value_history": { "ranked_values": ( { "rank": Rank.NONE, "value": None, "details": None }, { "rank": Rank.HARDCODED, "value": LogLevel.INFO, "details": None }, ), }, "typ": LogLevel, "default": LogLevel.INFO, "help": "Set the logging level.", "deprecation_active": False, "deprecated_message": None, "removal_version": None, "removal_hint": None, "choices": ("trace", "debug", "info", "warn", "error"), "comma_separated_choices": "trace, debug, info, warn, error", }, ), "advanced": tuple(), "deprecated": tuple(), }, "foo": { "scope": "foo", "provider": "help_info_extracter_test", "description": "A foo.", "is_goal": False, "deprecated_scope": None, "basic": (), "advanced": ({ "display_args": ("--[no-]foo-opt2", ), "comma_separated_display_args": "--[no-]foo-opt2", "scoped_cmd_line_args": ("--foo-opt2", "--no-foo-opt2"), "unscoped_cmd_line_args": ("--opt2", "--no-opt2"), "config_key": "opt2", "env_var": "PANTS_FOO_OPT2", "value_history": { "ranked_values": ( { "rank": Rank.NONE, "value": None, "details": None }, { "rank": Rank.HARDCODED, "value": True, "details": None }, ), }, "typ": bool, "default": True, "help": "Option 2", "deprecation_active": False, "deprecated_message": None, "removal_version": None, "removal_hint": None, "choices": None, "comma_separated_choices": None, }, ), "deprecated": tuple(), }, "bar": { "scope": "bar", "provider": "help_info_extracter_test", "description": "The bar goal.", "is_goal": True, "deprecated_scope": "bar-old", "basic": tuple(), "advanced": tuple(), "deprecated": tuple(), }, "bar-old": { "scope": "bar-old", "provider": "help_info_extracter_test", "description": "The bar goal.", "is_goal": True, "deprecated_scope": "bar-old", "basic": tuple(), "advanced": tuple(), "deprecated": tuple(), }, }, "name_to_goal_info": { "bar": { "name": "bar", "provider": "help_info_extracter_test", "description": "The bar goal.", "consumed_scopes": ("somescope", "used_by_bar"), "is_implemented": True, }, "bar-old": { "name": "bar", "provider": "help_info_extracter_test", "description": "The bar goal.", "consumed_scopes": ("somescope", "used_by_bar-old"), "is_implemented": True, }, }, "name_to_target_type_info": { "baz_library": { "alias": "baz_library", "provider": "help_info_extracter_test", "summary": "A library of baz-es.", "description": "A library of baz-es.\n\nUse it however you like.", "fields": ( { "alias": "qux", "provider": "", "default": "'blahblah'", "description": "A qux string.", "required": False, "type_hint": "str | None", }, { "alias": "quux", "provider": "", "default": None, "description": "A quux int.\n\nMust be non-zero. Or zero. " "Whatever you like really.", "required": True, "type_hint": "int", }, ), } }, "name_to_rule_info": { "construct_scope_foo": { "description": None, "documentation": "A foo.", "input_gets": ("Get(ScopedOptions, Scope, ..)", ), "input_types": (), "name": "construct_scope_foo", "output_type": "Foo", "provider": "help_info_extracter_test", }, "pants.help.help_info_extracter_test.test_get_all_help_info.rule_info_test": { "description": None, "documentation": "This rule is for testing info extraction only.", "input_gets": (), "input_types": ("Foo", ), "name": "pants.help.help_info_extracter_test.test_get_all_help_info.rule_info_test", "output_type": "Target", "provider": "help_info_extracter_test", }, }, "name_to_api_type_info": { "pants.help.help_info_extracter_test.Foo": { "consumed_by_rules": ("pants.help.help_info_extracter_test.test_get_all_help_info.rule_info_test", ), "dependees": ("help_info_extracter_test", ), "dependencies": ("pants.option.scope", ), "documentation": None, "is_union": False, "module": "pants.help.help_info_extracter_test", "name": "Foo", "provider": "help_info_extracter_test", "returned_by_rules": ("construct_scope_foo", ), "union_members": (), "union_type": None, "used_in_rules": (), }, "pants.engine.target.Target": { "consumed_by_rules": (), "dependees": (), "dependencies": (), "documentation": ("A Target represents an addressable set of metadata.\n\n Set the `help` " "class property with a description, which will be used in `./pants help`. For " "the\n best rendering, use soft wrapping (e.g. implicit string concatenation" ") within paragraphs, but\n hard wrapping (`\n`) to separate distinct " "paragraphs and/or lists.\n "), "is_union": False, "module": "pants.engine.target", "name": "Target", "provider": "help_info_extracter_test", "returned_by_rules": ("pants.help.help_info_extracter_test.test_get_all_help_info.rule_info_test", ), "union_members": (), "union_type": None, "used_in_rules": (), }, "pants.option.scope.Scope": { "consumed_by_rules": (), "dependees": (), "dependencies": (), "documentation": "An options scope.", "is_union": False, "module": "pants.option.scope", "name": "Scope", "provider": "pants.option.scope", "returned_by_rules": (), "union_members": (), "union_type": None, "used_in_rules": ("construct_scope_foo", ), }, }, } # Break down this colossal structure into pieces so it is easier to spot where the issue is. # Check keys equality first, then contents assert set(expected_all_help_info_dict) == set(all_help_info_dict) for key in all_help_info_dict: actual = all_help_info_dict[key] expected = expected_all_help_info_dict[key] assert expected == actual
def setup_graph_extended( build_configuration: BuildConfiguration, execution_options: ExecutionOptions, native: Native, *, executor: PyExecutor, pants_ignore_patterns: List[str], use_gitignore: bool, local_store_dir: str, local_execution_root_dir: str, named_caches_dir: str, ca_certs_path: Optional[str] = None, build_root: Optional[str] = None, include_trace_on_error: bool = True, native_engine_visualize_to: Optional[str] = None, ) -> GraphScheduler: build_root = build_root or get_buildroot() rules = build_configuration.rules union_membership = UnionMembership.from_rules( build_configuration.union_rules) registered_target_types = RegisteredTargetTypes.create( build_configuration.target_types) execution_options = execution_options or DEFAULT_EXECUTION_OPTIONS @rule def parser_singleton() -> Parser: return Parser( target_type_aliases=registered_target_types.aliases, object_aliases=build_configuration.registered_aliases, ) @rule def build_configuration_singleton() -> BuildConfiguration: return build_configuration @rule def registered_target_types_singleton() -> RegisteredTargetTypes: return registered_target_types @rule def union_membership_singleton() -> UnionMembership: return union_membership @rule def build_root_singleton() -> BuildRoot: return cast(BuildRoot, BuildRoot.instance) # Create a Scheduler containing graph and filesystem rules, with no installed goals. rules = FrozenOrderedSet(( *collect_rules(locals()), *build_files.rules(), *fs.rules(), *environment.rules(), *desktop.rules(), *graph.rules(), *options_parsing.rules(), *process.rules(), *platform.rules(), *changed_rules(), *streaming_workunit_handler_rules(), *specs_calculator.rules(), *rules, )) goal_map = EngineInitializer._make_goal_map_from_rules(rules) rules = FrozenOrderedSet(( *rules, # Install queries for each Goal. *(QueryRule(goal_type, GraphSession.goal_param_types) for goal_type in goal_map.values()), QueryRule(Snapshot, [PathGlobs]), # Used by the SchedulerService. )) def ensure_absolute_path(v: str) -> str: return Path(v).resolve().as_posix() def ensure_optional_absolute_path(v: Optional[str]) -> Optional[str]: if v is None: return None return ensure_absolute_path(v) scheduler = Scheduler( native=native, ignore_patterns=pants_ignore_patterns, use_gitignore=use_gitignore, build_root=build_root, local_store_dir=ensure_absolute_path(local_store_dir), local_execution_root_dir=ensure_absolute_path( local_execution_root_dir), named_caches_dir=ensure_absolute_path(named_caches_dir), ca_certs_path=ensure_optional_absolute_path(ca_certs_path), rules=rules, union_membership=union_membership, executor=executor, execution_options=execution_options, include_trace_on_error=include_trace_on_error, visualize_to_dir=native_engine_visualize_to, ) return GraphScheduler(scheduler, goal_map)
def test_get_all_help_info(): class Global(Subsystem): options_scope = GLOBAL_SCOPE help = "Global options." opt1 = IntOption("-o", "--opt1", default=42, help="Option 1") class Foo(Subsystem): options_scope = "foo" help = "A foo." opt2 = BoolOption("--opt2", default=True, advanced=True, help="Option 2") class Bar(GoalSubsystem): name = "bar" help = "The bar goal." deprecated_options_scope = "bar-old" deprecated_options_scope_removal_version = "9.9.999" class QuxField(StringField): alias = "qux" default = "blahblah" help = "A qux string." class QuuxField(IntField): alias = "quux" required = True help = "A quux int.\n\nMust be non-zero. Or zero. Whatever you like really." class BazLibrary(Target): alias = "baz_library" help = "A library of baz-es.\n\nUse it however you like." core_fields = [QuxField, QuuxField] options = Options.create( env={}, config=Config.load([]), known_scope_infos=[ Global.get_scope_info(), Foo.get_scope_info(), Bar.get_scope_info() ], args=["./pants"], bootstrap_option_values=None, ) Global.register_options_on_scope(options) Foo.register_options_on_scope(options) Bar.register_options_on_scope(options) @rule def rule_info_test(foo: Foo) -> Target: """This rule is for testing info extraction only.""" def fake_consumed_scopes_mapper(scope: str) -> Tuple[str, ...]: return ("somescope", f"used_by_{scope or 'GLOBAL_SCOPE'}") bc_builder = BuildConfiguration.Builder() bc_builder.register_subsystems("help_info_extracter_test", (Foo, Bar)) bc_builder.register_target_types("help_info_extracter_test", (BazLibrary, )) bc_builder.register_rules("help_info_extracter_test", collect_rules(locals())) all_help_info = HelpInfoExtracter.get_all_help_info( options, UnionMembership({}), fake_consumed_scopes_mapper, RegisteredTargetTypes({BazLibrary.alias: BazLibrary}), bc_builder.create(), ) all_help_info_dict = all_help_info.asdict() expected_all_help_info_dict = { "scope_to_help_info": { GLOBAL_SCOPE: { "scope": GLOBAL_SCOPE, "description": "Global options.", "provider": "", "is_goal": False, "deprecated_scope": None, "basic": ({ "display_args": ("-o=<int>", "--opt1=<int>"), "comma_separated_display_args": "-o=<int>, --opt1=<int>", "scoped_cmd_line_args": ("-o", "--opt1"), "unscoped_cmd_line_args": ("-o", "--opt1"), "config_key": "opt1", "env_var": "PANTS_OPT1", "value_history": { "ranked_values": ( { "rank": Rank.NONE, "value": None, "details": None }, { "rank": Rank.HARDCODED, "value": 42, "details": None }, ), }, "typ": int, "default": 42, "help": "Option 1", "deprecation_active": False, "deprecated_message": None, "removal_version": None, "removal_hint": None, "choices": None, "comma_separated_choices": None, }, ), "advanced": tuple(), "deprecated": tuple(), }, "foo": { "scope": "foo", "provider": "help_info_extracter_test", "description": "A foo.", "is_goal": False, "deprecated_scope": None, "basic": (), "advanced": ({ "display_args": ("--[no-]foo-opt2", ), "comma_separated_display_args": "--[no-]foo-opt2", "scoped_cmd_line_args": ("--foo-opt2", "--no-foo-opt2"), "unscoped_cmd_line_args": ("--opt2", "--no-opt2"), "config_key": "opt2", "env_var": "PANTS_FOO_OPT2", "value_history": { "ranked_values": ( { "rank": Rank.NONE, "value": None, "details": None }, { "rank": Rank.HARDCODED, "value": True, "details": None }, ), }, "typ": bool, "default": True, "help": "Option 2", "deprecation_active": False, "deprecated_message": None, "removal_version": None, "removal_hint": None, "choices": None, "comma_separated_choices": None, }, ), "deprecated": tuple(), }, "bar": { "scope": "bar", "provider": "help_info_extracter_test", "description": "The bar goal.", "is_goal": True, "deprecated_scope": "bar-old", "basic": tuple(), "advanced": tuple(), "deprecated": tuple(), }, "bar-old": { "scope": "bar-old", "provider": "help_info_extracter_test", "description": "The bar goal.", "is_goal": True, "deprecated_scope": "bar-old", "basic": tuple(), "advanced": tuple(), "deprecated": tuple(), }, }, "rule_output_type_to_rule_infos": { "Foo": ({ "description": None, "help": "A foo.", "input_gets": ("Get(ScopedOptions, Scope, ..)", ), "input_types": (), "name": "construct_scope_foo", "output_desc": None, "output_type": "Foo", "provider": "help_info_extracter_test", }, ), "Target": ({ "description": None, "help": "This rule is for testing info extraction only.", "input_gets": (), "input_types": ("Foo", ), "name": "pants.help.help_info_extracter_test.rule_info_test", "output_desc": ("A Target represents an addressable set of metadata.\n\n Set the " "`help` class property with a description, which will be used in " "`./pants help`. For the\n best rendering, use soft wrapping (e.g. " "implicit string concatenation) within paragraphs, but\n hard wrapping " "(`\n`) to separate distinct paragraphs and/or lists.\n "), "output_type": "Target", "provider": "help_info_extracter_test", }, ), }, "name_to_goal_info": { "bar": { "name": "bar", "provider": "help_info_extracter_test", "description": "The bar goal.", "consumed_scopes": ("somescope", "used_by_bar"), "is_implemented": True, }, "bar-old": { "name": "bar", "provider": "help_info_extracter_test", "description": "The bar goal.", "consumed_scopes": ("somescope", "used_by_bar-old"), "is_implemented": True, }, }, "name_to_target_type_info": { "baz_library": { "alias": "baz_library", "provider": "help_info_extracter_test", "summary": "A library of baz-es.", "description": "A library of baz-es.\n\nUse it however you like.", "fields": ( { "alias": "qux", "provider": "", "default": "'blahblah'", "description": "A qux string.", "required": False, "type_hint": "str | None", }, { "alias": "quux", "provider": "", "default": None, "description": "A quux int.\n\nMust be non-zero. Or zero. " "Whatever you like really.", "required": True, "type_hint": "int", }, ), } }, } assert expected_all_help_info_dict == all_help_info_dict
def setup_graph_extended( options_bootstrapper: OptionsBootstrapper, build_configuration: BuildConfiguration, execution_options: ExecutionOptions, native: Native, *, pants_ignore_patterns: List[str], use_gitignore: bool, local_store_dir: str, local_execution_root_dir: str, named_caches_dir: str, build_root: Optional[str] = None, include_trace_on_error: bool = True, ) -> GraphScheduler: build_root = build_root or get_buildroot() build_configuration = build_configuration or BuildConfigInitializer.get( options_bootstrapper) rules = build_configuration.rules union_membership = UnionMembership.from_rules( build_configuration.union_rules) registered_target_types = RegisteredTargetTypes.create( build_configuration.target_types) bootstrap_options = options_bootstrapper.bootstrap_options.for_global_scope( ) execution_options = execution_options or DEFAULT_EXECUTION_OPTIONS @rule def parser_singleton() -> Parser: return Parser( target_type_aliases=registered_target_types.aliases, object_aliases=build_configuration.registered_aliases, ) @rule def build_configuration_singleton() -> BuildConfiguration: return build_configuration @rule def registered_target_types_singleton() -> RegisteredTargetTypes: return registered_target_types @rule def union_membership_singleton() -> UnionMembership: return union_membership @rule def build_root_singleton() -> BuildRoot: return cast(BuildRoot, BuildRoot.instance) # Create a Scheduler containing graph and filesystem rules, with no installed goals. rules = FrozenOrderedSet(( *collect_rules(locals()), RootRule(Console), *build_files.rules(), *fs.rules(), *graph.rules(), *uuid.rules(), *options_parsing.rules(), *process.rules(), *create_platform_rules(), *changed_rules(), *rules, )) goal_map = EngineInitializer._make_goal_map_from_rules(rules) def ensure_absolute_path(v: str) -> str: return Path(v).resolve().as_posix() scheduler = Scheduler( native=native, ignore_patterns=pants_ignore_patterns, use_gitignore=use_gitignore, build_root=build_root, local_store_dir=ensure_absolute_path(local_store_dir), local_execution_root_dir=ensure_absolute_path( local_execution_root_dir), named_caches_dir=ensure_absolute_path(named_caches_dir), rules=rules, union_membership=union_membership, execution_options=execution_options, include_trace_on_error=include_trace_on_error, visualize_to_dir=bootstrap_options.native_engine_visualize_to, ) return GraphScheduler(scheduler, goal_map)
def test_get_all_help_info(): class Global(Subsystem): options_scope = GLOBAL_SCOPE help = "Global options." @classmethod def register_options(cls, register): register("-o", "--opt1", type=int, default=42, help="Option 1") class Foo(Subsystem): options_scope = "foo" help = "A foo." @classmethod def register_options(cls, register): register("--opt2", type=bool, default=True, help="Option 2") register("--opt3", advanced=True, choices=["a", "b", "c"]) class Bar(GoalSubsystem): name = "bar" help = "The bar goal." class QuxField(StringField): alias = "qux" default = "blahblah" help = "A qux string." class QuuxField(IntField): alias = "quux" required = True help = "A quux int.\n\nMust be non-zero. Or zero. Whatever you like really." class BazLibrary(Target): alias = "baz_library" help = "A library of baz-es.\n\nUse it however you like." core_fields = [QuxField, QuuxField] options = Options.create( env={}, config=Config.load_file_contents(""), known_scope_infos=[ Global.get_scope_info(), Foo.get_scope_info(), Bar.get_scope_info() ], args=["./pants"], bootstrap_option_values=None, ) Global.register_options_on_scope(options) Foo.register_options_on_scope(options) Bar.register_options_on_scope(options) def fake_consumed_scopes_mapper(scope: str) -> Tuple[str, ...]: return ("somescope", f"used_by_{scope or 'GLOBAL_SCOPE'}") all_help_info = HelpInfoExtracter.get_all_help_info( options, UnionMembership({}), fake_consumed_scopes_mapper, RegisteredTargetTypes({BazLibrary.alias: BazLibrary}), ) all_help_info_dict = dataclasses.asdict(all_help_info) expected_all_help_info_dict = { "scope_to_help_info": { GLOBAL_SCOPE: { "scope": GLOBAL_SCOPE, "description": "Global options.", "is_goal": False, "basic": ({ "display_args": ("-o=<int>", "--opt1=<int>"), "comma_separated_display_args": "-o=<int>, --opt1=<int>", "scoped_cmd_line_args": ("-o", "--opt1"), "unscoped_cmd_line_args": ("-o", "--opt1"), "config_key": "opt1", "env_var": "PANTS_OPT1", "value_history": { "ranked_values": ( { "rank": Rank.NONE, "value": None, "details": None }, { "rank": Rank.HARDCODED, "value": 42, "details": None }, ), }, "typ": int, "default": 42, "help": "Option 1", "deprecation_active": False, "deprecated_message": None, "removal_version": None, "removal_hint": None, "choices": None, "comma_separated_choices": None, }, ), "advanced": tuple(), "deprecated": tuple(), }, "foo": { "scope": "foo", "description": "A foo.", "is_goal": False, "basic": ({ "display_args": ("--[no-]foo-opt2", ), "comma_separated_display_args": "--[no-]foo-opt2", "scoped_cmd_line_args": ("--foo-opt2", "--no-foo-opt2"), "unscoped_cmd_line_args": ("--opt2", "--no-opt2"), "config_key": "opt2", "env_var": "PANTS_FOO_OPT2", "value_history": { "ranked_values": ( { "rank": Rank.NONE, "value": None, "details": None }, { "rank": Rank.HARDCODED, "value": True, "details": None }, ), }, "typ": bool, "default": True, "help": "Option 2", "deprecation_active": False, "deprecated_message": None, "removal_version": None, "removal_hint": None, "choices": None, "comma_separated_choices": None, }, ), "advanced": ({ "display_args": ("--foo-opt3=<str>", ), "comma_separated_display_args": "--foo-opt3=<str>", "scoped_cmd_line_args": ("--foo-opt3", ), "unscoped_cmd_line_args": ("--opt3", ), "config_key": "opt3", "env_var": "PANTS_FOO_OPT3", "value_history": { "ranked_values": ({ "rank": Rank.NONE, "value": None, "details": None }, ), }, "typ": str, "default": None, "help": "No help available.", "deprecation_active": False, "deprecated_message": None, "removal_version": None, "removal_hint": None, "choices": ("a", "b", "c"), "comma_separated_choices": "a, b, c", }, ), "deprecated": tuple(), }, "bar": { "scope": "bar", "description": "The bar goal.", "is_goal": True, "basic": tuple(), "advanced": tuple(), "deprecated": tuple(), }, }, "name_to_goal_info": { "bar": { "name": "bar", "description": "The bar goal.", "consumed_scopes": ("somescope", "used_by_bar"), "is_implemented": True, } }, "name_to_target_type_info": { "baz_library": { "alias": "baz_library", "summary": "A library of baz-es.", "description": "A library of baz-es.\n\nUse it however you like.", "fields": ( { "alias": "qux", "default": "'blahblah'", "description": "A qux string.", "required": False, "type_hint": "str | None", }, { "alias": "quux", "default": None, "description": "A quux int.\n\nMust be non-zero. Or zero. " "Whatever you like really.", "required": True, "type_hint": "int", }, ), } }, } assert expected_all_help_info_dict == all_help_info_dict