async def generate_pants_ini(console: Console, workspace: Workspace) -> GeneratePantsIni: pants_ini_content = dedent(f"""\ [GLOBAL] pants_version: {pants_version} """) preexisting_snapshot = await Get[Snapshot](PathGlobs(include=('pants.ini',))) if preexisting_snapshot.files: console.print_stderr( "./pants.ini already exists. This goal is only meant to be run the first time you run Pants " "in a project.\n\nTo update config values, please directly modify the file." ) return GeneratePantsIni(exit_code=1) console.print_stdout(dedent(f"""\ Adding sensible defaults to ./pants.ini: * Pinning `pants_version` to `{pants_version}`. """)) digest = await Get[Digest](InputFilesContent([ FileContent(path='pants.ini', content=pants_ini_content.encode()) ])) workspace.materialize_directory(DirectoryToMaterialize(digest)) console.print_stdout( "You may modify these values directly in the file at any time. The ./pants script will detect " "any changes the next time you run it.\n\nYou are now ready to use Pants!" ) return GeneratePantsIni(exit_code=0)
async def create_binary(addresses: BuildFileAddresses, console: Console, workspace: Workspace, options: Binary.Options, options_bootstrapper: OptionsBootstrapper, build_root: BuildRoot) -> Binary: with Binary.line_oriented(options, console) as print_stdout: global_options = options_bootstrapper.bootstrap_options.for_global_scope( ) pants_distdir = Path(global_options.pants_distdir) if not is_child_of(pants_distdir, build_root.pathlib_path): console.print_stderr( f"When set to an absolute path, `--pants-distdir` must be relative to the build root." "You set it to {pants_distdir}. Instead, use a relative path or an absolute path relative to the build root." ) return Binary(exit_code=1) relative_distdir = pants_distdir.relative_to( build_root.pathlib_path) if pants_distdir.is_absolute( ) else pants_distdir print_stdout(f"Generating binaries in `./{relative_distdir}`") binaries = await MultiGet( Get[CreatedBinary](Address, address.to_address()) for address in addresses) merged_digest = await Get[Digest](DirectoriesToMerge( tuple(binary.digest for binary in binaries))) result = workspace.materialize_directory( DirectoryToMaterialize(merged_digest, path_prefix=str(relative_distdir))) for path in result.output_paths: print_stdout(f"Wrote {path}") return Binary(exit_code=0)
async def run_tests( console: Console, options: TestOptions, runner: InteractiveRunner, addresses: BuildFileAddresses, ) -> Test: if options.values.debug: address = await Get[BuildFileAddress](BuildFileAddresses, addresses) addr_debug_request = await Get[AddressAndDebugRequest](Address, address.to_address()) result = runner.run_local_interactive_process(addr_debug_request.request.ipr) return Test(result.process_exit_code) results = await MultiGet(Get[AddressAndTestResult](Address, addr.to_address()) for addr in addresses) did_any_fail = False filtered_results = [(x.address, x.test_result) for x in results if x.test_result is not None] for address, test_result in filtered_results: if test_result.status == Status.FAILURE: did_any_fail = True if test_result.stdout: console.write_stdout(f"{address.reference()} stdout:\n{test_result.stdout}\n") if test_result.stderr: # NB: we write to stdout, rather than to stderr, to avoid potential issues interleaving the # two streams. console.write_stdout(f"{address.reference()} stderr:\n{test_result.stderr}\n") console.write_stdout("\n") for address, test_result in filtered_results: console.print_stdout(f'{address.reference():80}.....{test_result.status.value:>10}') if did_any_fail: console.print_stderr(console.red('\nTests failed')) exit_code = PANTS_FAILED_EXIT_CODE else: exit_code = PANTS_SUCCEEDED_EXIT_CODE return Test(exit_code)
async def fmt(console: Console, targets: HydratedTargets, workspace: Workspace, union_membership: UnionMembership) -> Fmt: results = await MultiGet( Get[FmtResult](TargetWithSources, target.adaptor) for target in targets if TargetWithSources.is_formattable_and_lintable( target.adaptor, union_membership=union_membership)) if not results: return Fmt(exit_code=0) # NB: this will fail if there are any conflicting changes, which we want to happen rather than # silently having one result override the other. # TODO(#8722): how should we handle multiple auto-formatters touching the same files? merged_formatted_digest = await Get[Digest](DirectoriesToMerge( tuple(result.digest for result in results))) workspace.materialize_directory( DirectoryToMaterialize(merged_formatted_digest)) for result in results: if result.stdout: console.print_stdout(result.stdout) if result.stderr: console.print_stderr(result.stderr) # Since the rules to produce FmtResult should use ExecuteRequest, rather than # FallibleExecuteProcessRequest, we assume that there were no failures. return Fmt(exit_code=0)
def fmt(console: Console, targets: HydratedTargets, union_membership: UnionMembership) -> Fmt: results = yield [ Get(FmtResult, TargetWithSources, target.adaptor) for target in targets # TODO: make TargetAdaptor return a 'sources' field with an empty snapshot instead of # raising to remove the hasattr() checks here! if union_membership.is_member(TargetWithSources, target.adaptor) and hasattr(target.adaptor, "sources") ] for result in results: files_content = yield Get(FilesContent, Digest, result.digest) # TODO: This is hacky and inefficient, and should be replaced by using the Workspace type # once that is available on master. # Blocked on: https://github.com/pantsbuild/pants/pull/8329 for file_content in files_content: with Path(get_buildroot(), file_content.path).open('wb') as f: f.write(file_content.content) if result.stdout: console.print_stdout(result.stdout) if result.stderr: console.print_stderr(result.stderr) # Since we ran an ExecuteRequest, any failure would already have interrupted our flow exit_code = 0 yield Fmt(exit_code)
def fast_test(console: Console, addresses: BuildFileAddresses) -> Test: test_results = yield [Get(TestResult, Address, address.to_address()) for address in addresses] did_any_fail = False for address, test_result in zip(addresses, test_results): if test_result.status == Status.FAILURE: did_any_fail = True if test_result.stdout: console.write_stdout( "{} stdout:\n{}\n".format( address.reference(), console.red(test_result.stdout) if test_result.status == Status.FAILURE else test_result.stdout ) ) if test_result.stderr: # NB: we write to stdout, rather than to stderr, to avoid potential issues interleaving the # two streams. console.write_stdout( "{} stderr:\n{}\n".format( address.reference(), console.red(test_result.stderr) if test_result.status == Status.FAILURE else test_result.stderr ) ) console.write_stdout("\n") for address, test_result in zip(addresses, test_results): console.print_stdout('{0:80}.....{1:>10}'.format(address.reference(), test_result.status.value)) if did_any_fail: console.print_stderr(console.red('Tests failed')) exit_code = PANTS_FAILED_EXIT_CODE else: exit_code = PANTS_SUCCEEDED_EXIT_CODE yield Test(exit_code)
async def coursier_resolve_lockfiles( console: Console, targets: Targets, resolve_subsystem: CoursierResolveSubsystem, workspace: Workspace, ) -> CoursierResolve: jvm_lockfile_targets = Targets( target for target in targets if target.has_field(JvmLockfileSources) ) results = await MultiGet( Get(CoursierGenerateLockfileResult, CoursierGenerateLockfileRequest(target=target)) for target in jvm_lockfile_targets ) # For performance reasons, avoid writing out files to the workspace that haven't changed. results_to_write = tuple(result for result in results if result.digest != EMPTY_DIGEST) if results_to_write: merged_digest = await Get( Digest, MergeDigests(result.digest for result in results_to_write) ) workspace.write_digest(merged_digest) merged_digest_snapshot = await Get(Snapshot, Digest, merged_digest) for path in merged_digest_snapshot.files: console.print_stderr(f"Updated lockfile at: {path}") return CoursierResolve(exit_code=0)
async def fmt(console: Console, targets: HydratedTargets, workspace: Workspace, union_membership: UnionMembership) -> Fmt: aggregated_results = await MultiGet( Get[AggregatedFmtResults](FormatTarget, target.adaptor) for target in targets if FormatTarget.is_formattable(target.adaptor, union_membership=union_membership)) individual_results = [ result for aggregated_result in aggregated_results for result in aggregated_result.results ] if not individual_results: return Fmt(exit_code=0) # NB: this will fail if there are any conflicting changes, which we want to happen rather than # silently having one result override the other. In practicality, this should never happen due # to our use of an aggregator rule for each distinct language. merged_formatted_digest = await Get[Digest](DirectoriesToMerge( tuple(aggregated_result.combined_digest for aggregated_result in aggregated_results))) workspace.materialize_directory( DirectoryToMaterialize(merged_formatted_digest)) for result in individual_results: if result.stdout: console.print_stdout(result.stdout) if result.stderr: console.print_stderr(result.stderr) # Since the rules to produce FmtResult should use ExecuteRequest, rather than # FallibleExecuteProcessRequest, we assume that there were no failures. return Fmt(exit_code=0)
def fmt(console: Console, targets: HydratedTargets) -> Fmt: results = yield [ Get(FmtResult, FmtTarget, target.adaptor) for target in targets # @union assumes that all targets passed implement the union, so we manually # filter the targets we know do; this should probably no-op or log or something # configurable for non-matching targets. # We also would want to remove the workaround that filters adaptors which have a # `sources` attribute. # See https://github.com/pantsbuild/pants/issues/4535 if isinstance(target.adaptor, ( PythonAppAdaptor, PythonTargetAdaptor, PythonTestsAdaptor, PythonBinaryAdaptor)) and hasattr(target.adaptor, "sources") ] for result in results: files_content = yield Get(FilesContent, Digest, result.digest) # TODO: This is hacky and inefficient, and should be replaced by using the Workspace type # once that is available on master. # Blocked on: https://github.com/pantsbuild/pants/pull/8329 for file_content in files_content: with Path(get_buildroot(), file_content.path).open('wb') as f: f.write(file_content.content) if result.stdout: console.print_stdout(result.stdout) if result.stderr: console.print_stderr(result.stderr) # Since we ran an ExecuteRequest, any failure would already have interrupted our flow exit_code = 0 yield Fmt(exit_code)
async def run_setup_pys(targets: HydratedTargets, options: SetupPyOptions, console: Console, provenance_map: AddressProvenanceMap, distdir: DistDir, workspace: Workspace) -> SetupPy: """Run setup.py commands on all exported targets addressed.""" args = tuple(options.values.args) validate_args(args) # Get all exported targets, ignoring any non-exported targets that happened to be # globbed over, but erroring on any explicitly-requested non-exported targets. exported_targets: List[ExportedTarget] = [] explicit_nonexported_targets: List[HydratedTarget] = [] for hydrated_target in targets: if _is_exported(hydrated_target): exported_targets.append(ExportedTarget(hydrated_target)) elif provenance_map.is_single_address(hydrated_target.address): explicit_nonexported_targets.append(hydrated_target) if explicit_nonexported_targets: raise TargetNotExported( 'Cannot run setup.py on these targets, because they have no `provides=` clause: ' f'{", ".join(so.address.reference() for so in explicit_nonexported_targets)}') if options.values.transitive: # Expand out to all owners of the entire dep closure. tht = await Get[TransitiveHydratedTargets]( BuildFileAddresses([et.hydrated_target.address for et in exported_targets])) owners = await MultiGet( Get[ExportedTarget](OwnedDependency(ht)) for ht in tht.closure if is_ownable_target(ht) ) exported_targets = list(set(owners)) chroots = await MultiGet(Get[SetupPyChroot](SetupPyChrootRequest(target)) for target in exported_targets) if args: setup_py_results = await MultiGet( Get[RunSetupPyResult](RunSetupPyRequest(exported_target, chroot, tuple(args))) for exported_target, chroot in zip(exported_targets, chroots) ) for exported_target, setup_py_result in zip(exported_targets, setup_py_results): addr = exported_target.hydrated_target.address.reference() console.print_stderr(f'Writing contents of dist dir for {addr} to {distdir.relpath}') workspace.materialize_directory( DirectoryToMaterialize(setup_py_result.output, path_prefix=str(distdir.relpath)) ) else: # Just dump the chroot. for exported_target, chroot in zip(exported_targets, chroots): addr = exported_target.hydrated_target.address.reference() provides = exported_target.hydrated_target.adaptor.provides setup_py_dir = distdir.relpath / f'{provides.name}-{provides.version}' console.print_stderr(f'Writing setup.py chroot for {addr} to {setup_py_dir}') workspace.materialize_directory( DirectoryToMaterialize(chroot.digest, path_prefix=str(setup_py_dir)) ) return SetupPy(0)
def materialize(self, console: Console, workspace: Workspace) -> Optional[PurePath]: workspace.write_digest( self.result_snapshot.digest, path_prefix=str(self.directory_to_materialize_to) ) console.print_stderr( f"\nWrote {self.report_type} coverage report to `{self.directory_to_materialize_to}`" ) return self.report_file
async def list_targets(console: Console, list_options: ListOptions, addresses: Addresses) -> List: provides = list_options.values.provides provides_columns = list_options.values.provides_columns documented = list_options.values.documented collection: Union[HydratedTargets, Addresses] if provides or documented: # To get provides clauses or documentation, we need hydrated targets. collection = await Get[HydratedTargets](Addresses, addresses) if provides: extractors = dict( address=lambda adaptor: adaptor.address.spec, artifact_id=lambda adaptor: str(adaptor.provides), repo_name=lambda adaptor: adaptor.provides.repo.name, repo_url=lambda adaptor: adaptor.provides.repo.url, push_db_basedir=lambda adaptor: adaptor.provides.repo.push_db_basedir, ) def print_provides(col_extractors, target): if getattr(target.adaptor, "provides", None): return " ".join(extractor(target.adaptor) for extractor in col_extractors) try: column_extractors = [extractors[col] for col in (provides_columns.split(","))] except KeyError: raise Exception( "Invalid columns specified: {0}. Valid columns are: address, artifact_id, " "repo_name, repo_url, push_db_basedir.".format(provides_columns) ) print_fn = lambda target: print_provides(column_extractors, target) else: def print_documented(target): description = getattr(target.adaptor, "description", None) if description: return "{0}\n {1}".format( target.adaptor.address.spec, "\n ".join(description.strip().split("\n")) ) print_fn = print_documented else: # Otherwise, we can use only addresses. collection = addresses print_fn = lambda address: address.spec with list_options.line_oriented(console) as print_stdout: if not collection: console.print_stderr("WARNING: No targets were matched in goal `{}`.".format("list")) for item in collection: result = print_fn(item) if result: print_stdout(result) return List(exit_code=0)
async def lint( console: Console, targets_with_origins: TargetsWithOrigins, options: LintOptions, union_membership: UnionMembership, ) -> Lint: config_collection_types: Iterable[ Type[LinterConfigurations]] = union_membership.union_rules[ LinterConfigurations] config_collections: Iterable[LinterConfigurations] = tuple( config_collection_type( config_collection_type.config_type.create(target_with_origin) for target_with_origin in targets_with_origins if config_collection_type.config_type.is_valid( target_with_origin.target)) for config_collection_type in config_collection_types) config_collections_with_sources: Iterable[ ConfigurationsWithSources] = await MultiGet( Get[ConfigurationsWithSources](ConfigurationsWithSourcesRequest( config_collection)) for config_collection in config_collections) # NB: We must convert back the generic ConfigurationsWithSources objects back into their # corresponding LinterConfigurations, e.g. back to IsortConfigurations, in order for the union # rule to work. valid_config_collections: Iterable[LinterConfigurations] = tuple( config_collection_cls(config_collection) for config_collection_cls, config_collection in zip( config_collection_types, config_collections_with_sources) if config_collection) if options.values.per_target_caching: results = await MultiGet( Get[LintResult](LinterConfigurations, config_collection.__class__([config])) for config_collection in valid_config_collections for config in config_collection) else: results = await MultiGet( Get[LintResult](LinterConfigurations, config_collection) for config_collection in valid_config_collections) if not results: return Lint(exit_code=0) exit_code = 0 for result in results: if result.stdout: console.print_stdout(result.stdout) if result.stderr: console.print_stderr(result.stderr) if result.exit_code != 0: exit_code = result.exit_code return Lint(exit_code)
async def run_repl( console: Console, workspace: Workspace, repl_subsystem: ReplSubsystem, all_specified_addresses: Addresses, build_root: BuildRoot, union_membership: UnionMembership, global_options: GlobalOptions, complete_env: CompleteEnvironment, ) -> Repl: transitive_targets = await Get( TransitiveTargets, TransitiveTargetsRequest(all_specified_addresses)) # TODO: When we support multiple languages, detect the default repl to use based # on the targets. For now we default to the python repl. repl_shell_name = repl_subsystem.shell or "python" implementations = { impl.name: impl for impl in union_membership[ReplImplementation] } repl_implementation_cls = implementations.get(repl_shell_name) if repl_implementation_cls is None: available = sorted(implementations.keys()) console.print_stderr( f"{repr(repl_shell_name)} is not a registered REPL. Available REPLs (which may " f"be specified through the option `--repl-shell`): {available}") return Repl(-1) with temporary_dir(root_dir=global_options.options.pants_workdir, cleanup=False) as tmpdir: repl_impl = repl_implementation_cls(targets=Targets( transitive_targets.closure), chroot=tmpdir) request = await Get(ReplRequest, ReplImplementation, repl_impl) workspace.write_digest( request.digest, path_prefix=PurePath(tmpdir).relative_to( build_root.path).as_posix(), # We don't want to influence whether the InteractiveProcess is able to restart. Because # we're writing into a temp directory, we can safely mark this side_effecting=False. side_effecting=False, ) env = {**complete_env, **request.extra_env} result = await Effect( InteractiveProcessResult, InteractiveProcess( argv=request.args, env=env, run_in_workspace=True, restartable=repl_subsystem.restartable, ), ) return Repl(result.exit_code)
async def check( console: Console, workspace: Workspace, targets: Targets, dist_dir: DistDir, union_membership: UnionMembership, ) -> Check: typecheck_request_types = cast("Iterable[type[StyleRequest]]", union_membership[CheckRequest]) requests = tuple( typecheck_request_type( typecheck_request_type.field_set_type.create(target) for target in targets if typecheck_request_type.field_set_type.is_applicable(target)) for typecheck_request_type in typecheck_request_types) field_sets_with_sources = await MultiGet( Get(FieldSetsWithSources, FieldSetsWithSourcesRequest(request.field_sets)) for request in requests) valid_requests = tuple( request_cls(request) for request_cls, request in zip( typecheck_request_types, field_sets_with_sources) if request) all_results = await MultiGet( Get(CheckResults, CheckRequest, request) for request in valid_requests) def get_tool_name(res: CheckResults) -> str: return res.checker_name write_reports( all_results, workspace, dist_dir, goal_name=CheckSubsystem.name, get_tool_name=get_tool_name, ) exit_code = 0 if all_results: console.print_stderr("") for results in sorted(all_results, key=lambda results: results.checker_name): if results.skipped: sigil = console.sigil_skipped() status = "skipped" elif results.exit_code == 0: sigil = console.sigil_succeeded() status = "succeeded" else: sigil = console.sigil_failed() status = "failed" exit_code = results.exit_code console.print_stderr(f"{sigil} {results.checker_name} {status}.") return Check(exit_code)
async def check( console: Console, workspace: Workspace, targets: FilteredTargets, dist_dir: DistDir, union_membership: UnionMembership, check_subsystem: CheckSubsystem, ) -> Check: request_types = cast("Iterable[type[StyleRequest]]", union_membership[CheckRequest]) specified_names = determine_specified_tool_names("check", check_subsystem.only, request_types) requests = tuple( request_type( request_type.field_set_type.create(target) for target in targets if ( request_type.name in specified_names and request_type.field_set_type.is_applicable(target) ) ) for request_type in request_types ) all_results = await MultiGet( Get(CheckResults, CheckRequest, request) for request in requests if request.field_sets ) def get_name(res: CheckResults) -> str: return res.checker_name write_reports( all_results, workspace, dist_dir, goal_name=CheckSubsystem.name, get_name=get_name, ) exit_code = 0 if all_results: console.print_stderr("") for results in sorted(all_results, key=lambda results: results.checker_name): if results.skipped: continue elif results.exit_code == 0: sigil = console.sigil_succeeded() status = "succeeded" else: sigil = console.sigil_failed() status = "failed" exit_code = results.exit_code console.print_stderr(f"{sigil} {results.checker_name} {status}.") return Check(exit_code)
async def run_repl( console: Console, workspace: Workspace, runner: InteractiveRunner, options: ReplOptions, transitive_targets: TransitiveTargets, build_root: BuildRoot, union_membership: UnionMembership, global_options: GlobalOptions, ) -> Repl: # We can guarantee that we will only even enter this `goal_rule` if there exists an implementer # of the `ReplImplementation` union because `LegacyGraphSession.run_goal_rules()` will not # execute this rule's body if there are no implementations registered. membership: Iterable[Type[ ReplImplementation]] = union_membership.union_rules[ReplImplementation] implementations = {impl.name: impl for impl in membership} default_repl = "python" repl_shell_name = cast(str, options.values.shell or default_repl) repl_implementation_cls = implementations.get(repl_shell_name) if repl_implementation_cls is None: available = sorted(set(implementations.keys())) console.print_stderr( f"{repr(repl_shell_name)} is not a registered REPL. Available REPLs (which may " f"be specified through the option `--repl-shell`): {available}") return Repl(-1) repl_impl = repl_implementation_cls(targets=Targets( tgt for tgt in transitive_targets.closure if repl_implementation_cls.is_valid(tgt))) repl_binary = await Get[ReplBinary](ReplImplementation, repl_impl) with temporary_dir(root_dir=global_options.options.pants_workdir, cleanup=False) as tmpdir: path_relative_to_build_root = PurePath(tmpdir).relative_to( build_root.path).as_posix() workspace.materialize_directory( DirectoryToMaterialize(repl_binary.digest, path_prefix=path_relative_to_build_root)) full_path = PurePath(tmpdir, repl_binary.binary_name).as_posix() run_request = InteractiveProcessRequest( argv=(full_path, ), run_in_workspace=True, ) result = runner.run_local_interactive_process(run_request) return Repl(result.process_exit_code)
async def run( run_subsystem: RunSubsystem, global_options: GlobalOptions, console: Console, interactive_runner: InteractiveRunner, workspace: Workspace, build_root: BuildRoot, complete_env: CompleteEnvironment, ) -> Run: targets_to_valid_field_sets = await Get( TargetRootsToFieldSets, TargetRootsToFieldSetsRequest( RunFieldSet, goal_description="the `run` goal", no_applicable_targets_behavior=NoApplicableTargetsBehavior.error, expect_single_field_set=True, ), ) field_set = targets_to_valid_field_sets.field_sets[0] request = await Get(RunRequest, RunFieldSet, field_set) with temporary_dir(root_dir=global_options.options.pants_workdir, cleanup=True) as tmpdir: workspace.write_digest(request.digest, path_prefix=PurePath(tmpdir).relative_to( build_root.path).as_posix()) args = (arg.format(chroot=tmpdir) for arg in request.args) env = { **complete_env, **{ k: v.format(chroot=tmpdir) for k, v in request.extra_env.items() } } try: result = interactive_runner.run( InteractiveProcess( argv=(*args, *run_subsystem.args), env=env, run_in_workspace=True, )) exit_code = result.exit_code except Exception as e: console.print_stderr( f"Exception when attempting to run {field_set.address}: {e!r}") exit_code = -1 return Run(exit_code)
async def validate( console: Console, sources_snapshots: SourcesSnapshots, validate_options: ValidateOptions, ) -> Validate: per_snapshot_rmrs = await MultiGet( Get[RegexMatchResults](SourcesSnapshot, source_snapshot) for source_snapshot in sources_snapshots) regex_match_results = list(itertools.chain(*per_snapshot_rmrs)) detail_level = validate_options.values.detail_level regex_match_results = sorted(regex_match_results, key=lambda x: x.path) num_matched_all = 0 num_nonmatched_some = 0 for rmr in regex_match_results: if not rmr.matching and not rmr.nonmatching: continue if rmr.nonmatching: icon = "X" num_nonmatched_some += 1 else: icon = "V" num_matched_all += 1 matched_msg = " Matched: {}".format(",".join( rmr.matching)) if rmr.matching else "" nonmatched_msg = (" Didn't match: {}".format(",".join(rmr.nonmatching)) if rmr.nonmatching else "") if detail_level == DetailLevel.all or ( detail_level == DetailLevel.nonmatching and nonmatched_msg): console.print_stdout("{} {}:{}{}".format(icon, rmr.path, matched_msg, nonmatched_msg)) if detail_level != DetailLevel.none: console.print_stdout( "\n{} files matched all required patterns.".format( num_matched_all)) console.print_stdout( "{} files failed to match at least one required pattern.".format( num_nonmatched_some)) if num_nonmatched_some: console.print_stderr("Files failed validation.") exit_code = PANTS_FAILED_EXIT_CODE else: exit_code = PANTS_SUCCEEDED_EXIT_CODE return Validate(exit_code)
async def lint( console: Console, targets_with_origins: TargetsWithOrigins, options: LintOptions, union_membership: UnionMembership, ) -> Lint: lint_request_types = union_membership[LintRequest] lint_requests: Iterable[StyleRequest] = tuple( lint_request_type( lint_request_type.field_set_type.create(target_with_origin) for target_with_origin in targets_with_origins if lint_request_type.field_set_type.is_valid(target_with_origin.target) ) for lint_request_type in union_membership[LintRequest] ) field_sets_with_sources: Iterable[FieldSetsWithSources] = await MultiGet( Get[FieldSetsWithSources](FieldSetsWithSourcesRequest(lint_request.field_sets)) for lint_request in lint_requests ) valid_lint_requests: Iterable[StyleRequest] = tuple( lint_request_cls(lint_request) for lint_request_cls, lint_request in zip(lint_request_types, field_sets_with_sources) if lint_request ) if options.values.per_target_caching: results = await MultiGet( Get[LintResults](LintRequest, lint_request.__class__([field_set])) for lint_request in valid_lint_requests for field_set in lint_request.field_sets ) else: results = await MultiGet( Get[LintResults](LintRequest, lint_request) for lint_request in valid_lint_requests ) if not results: return Lint(exit_code=0) exit_code = 0 sorted_results = sorted(itertools.chain.from_iterable(results), key=lambda res: res.linter_name) for result in sorted_results: console.print_stderr( f"{console.green('✓')} {result.linter_name} succeeded." if result.exit_code == 0 else f"{console.red('𐄂')} {result.linter_name} failed." ) if result.stdout: console.print_stderr(result.stdout) if result.stderr: console.print_stderr(result.stderr) if result != sorted_results[-1]: console.print_stderr("") if result.exit_code != 0: exit_code = result.exit_code return Lint(exit_code)
async def run_repl( console: Console, workspace: Workspace, runner: InteractiveRunner, options: ReplOptions, transitive_targets: TransitiveTargets, build_root: BuildRoot, union_membership: UnionMembership, global_options: GlobalOptions, ) -> Repl: default_repl = "python" repl_shell_name = cast(str, options.values.shell) or default_repl implementations: Dict[str, Type[ReplImplementation]] = { impl.name: impl for impl in union_membership[ReplImplementation] } repl_implementation_cls = implementations.get(repl_shell_name) if repl_implementation_cls is None: available = sorted(implementations.keys()) console.print_stderr( f"{repr(repl_shell_name)} is not a registered REPL. Available REPLs (which may " f"be specified through the option `--repl-shell`): {available}") return Repl(-1) repl_impl = repl_implementation_cls(targets=Targets( tgt for tgt in transitive_targets.closure if repl_implementation_cls.is_valid(tgt))) repl_binary = await Get[ReplBinary](ReplImplementation, repl_impl) with temporary_dir(root_dir=global_options.options.pants_workdir, cleanup=False) as tmpdir: path_relative_to_build_root = PurePath(tmpdir).relative_to( build_root.path).as_posix() workspace.materialize_directory( DirectoryToMaterialize(repl_binary.digest, path_prefix=path_relative_to_build_root)) full_path = PurePath(tmpdir, repl_binary.binary_name).as_posix() run_request = InteractiveProcessRequest( argv=(full_path, ), run_in_workspace=True, ) result = runner.run_local_interactive_process(run_request) return Repl(result.process_exit_code)
async def run_repl( console: Console, workspace: Workspace, interactive_runner: InteractiveRunner, repl_subsystem: ReplSubsystem, all_specified_addresses: Addresses, build_root: BuildRoot, union_membership: UnionMembership, global_options: GlobalOptions, ) -> Repl: transitive_targets = await Get( TransitiveTargets, TransitiveTargetsRequest(all_specified_addresses)) # TODO: When we support multiple languages, detect the default repl to use based # on the targets. For now we default to the python repl. repl_shell_name = repl_subsystem.shell or "python" implementations: Dict[str, Type[ReplImplementation]] = { impl.name: impl for impl in union_membership[ReplImplementation] } repl_implementation_cls = implementations.get(repl_shell_name) if repl_implementation_cls is None: available = sorted(implementations.keys()) console.print_stderr( f"{repr(repl_shell_name)} is not a registered REPL. Available REPLs (which may " f"be specified through the option `--repl-shell`): {available}") return Repl(-1) with temporary_dir(root_dir=global_options.options.pants_workdir, cleanup=False) as tmpdir: repl_impl = repl_implementation_cls(targets=Targets( transitive_targets.closure), chroot=tmpdir) request = await Get(ReplRequest, ReplImplementation, repl_impl) workspace.write_digest(request.digest, path_prefix=PurePath(tmpdir).relative_to( build_root.path).as_posix()) result = interactive_runner.run( InteractiveProcess(argv=request.args, env=request.extra_env, run_in_workspace=True, hermetic_env=False)) return Repl(result.exit_code)
async def list_targets(addresses: Addresses, list_subsystem: ListSubsystem, console: Console) -> List: if not addresses: console.print_stderr( f"WARNING: No targets were matched in goal `{list_subsystem.name}`." ) return List(exit_code=0) if list_subsystem.provides and list_subsystem.documented: raise ValueError( "Cannot specify both `--list-documented` and `--list-provides` at the same time. " "Please choose one.") if list_subsystem.provides: targets = await Get(UnexpandedTargets, Addresses, addresses) addresses_with_provide_artifacts = { tgt.address: tgt[ProvidesField].value for tgt in targets if tgt.get(ProvidesField).value is not None } with list_subsystem.line_oriented(console) as print_stdout: for address, artifact in addresses_with_provide_artifacts.items(): print_stdout(f"{address.spec} {artifact}") return List(exit_code=0) if list_subsystem.documented: targets = await Get(UnexpandedTargets, Addresses, addresses) addresses_with_descriptions = cast( Dict[Address, str], { tgt.address: tgt[DescriptionField].value for tgt in targets if tgt.get(DescriptionField).value is not None }, ) with list_subsystem.line_oriented(console) as print_stdout: for address, description in addresses_with_descriptions.items(): formatted_description = "\n ".join( description.strip().split("\n")) print_stdout(f"{address.spec}\n {formatted_description}") return List(exit_code=0) with list_subsystem.line_oriented(console) as print_stdout: for address in sorted(addresses): print_stdout(address.spec) return List(exit_code=0)
def ui_open(console: Console, runner: InteractiveRunner, files: Iterable[PurePath]) -> None: """Opens the given files with the appropriate application for the current operating system. Any failures to either locate an appropriate application to open the files with or else execute that program are reported to the console stderr. """ osname = get_os_name() opener_type = _OPENERS_BY_OSNAME.get(osname) if opener_type is None: console.print_stderr(f"Could not open {' '.join(map(str, files))} for viewing.") console.print_stderr( f"Opening files for viewing is currently not supported for the " f"{osname} operating system." ) return opener = opener_type(console, runner) opener.open(files)
def validate(console: Console, hydrated_targets: HydratedTargets, validate_options: Validate.Options) -> Validate: per_tgt_rmrs = yield [ Get(RegexMatchResults, HydratedTarget, ht) for ht in hydrated_targets ] regex_match_results = list(itertools.chain(*per_tgt_rmrs)) detail_level = validate_options.values.detail_level regex_match_results = sorted(regex_match_results, key=lambda x: x.path) num_matched_all = 0 num_nonmatched_some = 0 for rmr in regex_match_results: if not rmr.matching and not rmr.nonmatching: continue if rmr.nonmatching: icon = 'X' num_nonmatched_some += 1 else: icon = 'V' num_matched_all += 1 matched_msg = ' Matched: {}'.format(','.join( rmr.matching)) if rmr.matching else '' nonmatched_msg = (" Didn't match: {}".format(','.join(rmr.nonmatching)) if rmr.nonmatching else '') if (detail_level == DetailLevel.all or (detail_level == DetailLevel.nonmatching and nonmatched_msg)): console.print_stdout("{} {}:{}{}".format(icon, rmr.path, matched_msg, nonmatched_msg)) if detail_level != DetailLevel.none: console.print_stdout( '\n{} files matched all required patterns.'.format( num_matched_all)) console.print_stdout( '{} files failed to match at least one required pattern.'.format( num_nonmatched_some)) if num_nonmatched_some: console.print_stderr('Files failed validation.') exit_code = PANTS_FAILED_EXIT_CODE else: exit_code = PANTS_SUCCEEDED_EXIT_CODE yield Validate(exit_code)
async def lint( console: Console, targets_with_origins: HydratedTargetsWithOrigins, options: LintOptions, union_membership: UnionMembership, ) -> Lint: adaptors_with_origins = tuple( TargetAdaptorWithOrigin.create(target_with_origin.target.adaptor, target_with_origin.origin) for target_with_origin in targets_with_origins if target_with_origin.target.adaptor.has_sources()) linters: Iterable[Type[Linter]] = union_membership.union_rules[Linter] if options.values.per_target_caching: results = await MultiGet( Get[LintResult](Linter, linter((adaptor_with_origin, ))) for adaptor_with_origin in adaptors_with_origins for linter in linters if linter.is_valid_target(adaptor_with_origin)) else: linters_with_valid_targets = { linter: tuple(adaptor_with_origin for adaptor_with_origin in adaptors_with_origins if linter.is_valid_target(adaptor_with_origin)) for linter in linters } results = await MultiGet( Get[LintResult](Linter, linter(valid_targets)) for linter, valid_targets in linters_with_valid_targets.items() if valid_targets) if not results: return Lint(exit_code=0) exit_code = 0 for result in results: if result.stdout: console.print_stdout(result.stdout) if result.stderr: console.print_stderr(result.stderr) if result.exit_code != 0: exit_code = result.exit_code return Lint(exit_code)
async def lint( console: Console, workspace: Workspace, targets: Targets, lint_subsystem: LintSubsystem, union_membership: UnionMembership, ) -> Lint: request_types = union_membership[LintRequest] requests: Iterable[StyleRequest] = tuple( request_type( request_type.field_set_type.create(target) for target in targets if request_type.field_set_type.is_valid(target)) for request_type in request_types) field_sets_with_sources: Iterable[FieldSetsWithSources] = await MultiGet( Get(FieldSetsWithSources, FieldSetsWithSourcesRequest(request.field_sets)) for request in requests) valid_requests: Iterable[StyleRequest] = tuple( request_cls(request) for request_cls, request in zip(request_types, field_sets_with_sources) if request) if lint_subsystem.per_target_caching: results = await MultiGet( Get(LintResults, LintRequest, request.__class__([field_set])) for request in valid_requests for field_set in request.field_sets) else: results = await MultiGet( Get(LintResults, LintRequest, lint_request) for lint_request in valid_requests) sorted_results = sorted(itertools.chain.from_iterable(results), key=lambda res: res.linter_name) if not sorted_results: return Lint(exit_code=0) exit_code = 0 for result in sorted_results: console.print_stderr( f"{console.green('✓')} {result.linter_name} succeeded." if result. exit_code == 0 else f"{console.red('𐄂')} {result.linter_name} failed.") if result.stdout: console.print_stderr(result.stdout) if result.stderr: console.print_stderr(result.stderr) if result != sorted_results[-1]: console.print_stderr("") result.materialize(console, workspace) if result.exit_code != 0: exit_code = result.exit_code return Lint(exit_code)
async def run_repl( console: Console, workspace: Workspace, interactive_runner: InteractiveRunner, repl_subsystem: ReplSubsystem, transitive_targets: TransitiveTargets, build_root: BuildRoot, union_membership: UnionMembership, global_options: GlobalOptions, ) -> Repl: repl_shell_name = repl_subsystem.shell or "python" implementations: Dict[str, Type[ReplImplementation]] = { impl.name: impl for impl in union_membership[ReplImplementation] } repl_implementation_cls = implementations.get(repl_shell_name) if repl_implementation_cls is None: available = sorted(implementations.keys()) console.print_stderr( f"{repr(repl_shell_name)} is not a registered REPL. Available REPLs (which may " f"be specified through the option `--repl-shell`): {available}") return Repl(-1) repl_impl = repl_implementation_cls(targets=Targets( tgt for tgt in transitive_targets.closure if repl_implementation_cls.is_valid(tgt))) request = await Get(ReplRequest, ReplImplementation, repl_impl) with temporary_dir(root_dir=global_options.options.pants_workdir, cleanup=False) as tmpdir: tmpdir_relative_path = PurePath(tmpdir).relative_to( build_root.path).as_posix() exe_path = PurePath(tmpdir, request.binary_name).as_posix() workspace.write_digest(request.digest, path_prefix=tmpdir_relative_path) result = interactive_runner.run( InteractiveProcess(argv=(exe_path, ), env=request.env, run_in_workspace=True)) return Repl(result.exit_code)
async def run( run_subsystem: RunSubsystem, global_options: GlobalOptions, console: Console, interactive_runner: InteractiveRunner, workspace: Workspace, build_root: BuildRoot, ) -> Run: targets_to_valid_field_sets = await Get( TargetsToValidFieldSets, TargetsToValidFieldSetsRequest( BinaryFieldSet, goal_description="the `run` goal", error_if_no_valid_targets=True, expect_single_field_set=True, ), ) field_set = targets_to_valid_field_sets.field_sets[0] request = await Get(RunRequest, BinaryFieldSet, field_set) with temporary_dir(root_dir=global_options.options.pants_workdir, cleanup=True) as tmpdir: tmpdir_relative_path = PurePath(tmpdir).relative_to( build_root.path).as_posix() workspace.write_digest(request.digest, path_prefix=tmpdir_relative_path) exe_path = PurePath(tmpdir, request.binary_name).as_posix() process = InteractiveProcess( argv=(exe_path, *request.prefix_args, *run_subsystem.args), env=request.env, run_in_workspace=True, ) try: result = interactive_runner.run(process) exit_code = result.exit_code except Exception as e: console.print_stderr( f"Exception when attempting to run {field_set.address}: {e!r}") exit_code = -1 return Run(exit_code)
async def typecheck( console: Console, targets: Targets, union_membership: UnionMembership ) -> Typecheck: typecheck_request_types = union_membership[TypecheckRequest] requests: Iterable[StyleRequest] = tuple( lint_request_type( lint_request_type.field_set_type.create(target) for target in targets if lint_request_type.field_set_type.is_applicable(target) ) for lint_request_type in typecheck_request_types ) field_sets_with_sources: Iterable[FieldSetsWithSources] = await MultiGet( Get(FieldSetsWithSources, FieldSetsWithSourcesRequest(request.field_sets)) for request in requests ) valid_requests: Iterable[StyleRequest] = tuple( request_cls(request) for request_cls, request in zip(typecheck_request_types, field_sets_with_sources) if request ) all_results = await MultiGet( Get(TypecheckResults, TypecheckRequest, request) for request in valid_requests ) exit_code = 0 if all_results: console.print_stderr("") for results in sorted(all_results, key=lambda results: results.typechecker_name): if results.skipped: sigil = console.yellow("-") status = "skipped" elif results.exit_code == 0: sigil = console.green("✓") status = "succeeded" else: sigil = console.red("𐄂") status = "failed" exit_code = results.exit_code console.print_stderr(f"{sigil} {results.typechecker_name} {status}.") return Typecheck(exit_code)