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)
async def fmt( console: Console, targets: Targets, fmt_subsystem: FmtSubsystem, workspace: Workspace, union_membership: UnionMembership, ) -> Fmt: language_target_collection_types = union_membership[LanguageFmtTargets] language_target_collections: Iterable[LanguageFmtTargets] = tuple( language_target_collection_type( Targets(target for target in targets if language_target_collection_type.belongs_to_language( target))) for language_target_collection_type in language_target_collection_types) targets_with_sources: Iterable[TargetsWithSources] = await MultiGet( Get( TargetsWithSources, TargetsWithSourcesRequest(language_target_collection.targets), ) for language_target_collection in language_target_collections) # NB: We must convert back the generic TargetsWithSources objects back into their # corresponding LanguageFmtTargets, e.g. back to PythonFmtTargets, in order for the union # rule to work. valid_language_target_collections: Iterable[LanguageFmtTargets] = tuple( language_target_collection_cls( Targets(target for target in language_target_collection.targets if target in language_targets_with_sources)) for language_target_collection_cls, language_target_collection, language_targets_with_sources in zip( language_target_collection_types, language_target_collections, targets_with_sources) if language_targets_with_sources) if fmt_subsystem.per_file_caching: per_language_results = await MultiGet( Get( LanguageFmtResults, LanguageFmtTargets, language_target_collection.__class__(Targets([target])), ) for language_target_collection in valid_language_target_collections for target in language_target_collection.targets) else: per_language_results = await MultiGet( Get(LanguageFmtResults, LanguageFmtTargets, language_target_collection) for language_target_collection in valid_language_target_collections ) individual_results: List[FmtResult] = list( itertools.chain.from_iterable( language_result.results for language_result in per_language_results)) if not individual_results: return Fmt(exit_code=0) changed_digests = tuple(language_result.output for language_result in per_language_results if language_result.did_change) if changed_digests: # 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 practice, this should never # happen due to us grouping each language's formatters into a single digest. merged_formatted_digest = await Get(Digest, MergeDigests(changed_digests)) workspace.write_digest(merged_formatted_digest) if individual_results: console.print_stderr("") # We group all results for the same formatter so that we can give one final status in the # summary. This is only relevant if there were multiple results because of # `--per-file-caching`. formatter_to_results = defaultdict(set) for result in individual_results: formatter_to_results[result.formatter_name].add(result) for formatter, results in sorted(formatter_to_results.items()): if any(result.did_change for result in results): sigil = console.red("𐄂") status = "made changes" elif all(result.skipped for result in results): sigil = console.yellow("-") status = "skipped" else: sigil = console.green("✓") status = "made no changes" console.print_stderr(f"{sigil} {formatter} {status}.") # Since the rules to produce FmtResult should use ExecuteRequest, rather than # FallibleProcess, we assume that there were no failures. return Fmt(exit_code=0)
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_applicable(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_file_caching: all_per_file_results = await MultiGet( Get(LintResults, LintRequest, request.__class__([field_set])) for request in valid_requests for field_set in request.field_sets) def key_fn(results: LintResults): return results.linter_name # NB: We must pre-sort the data for itertools.groupby() to work properly. sorted_all_per_files_results = sorted(all_per_file_results, key=key_fn) # We consolidate all results for each linter into a single `LintResults`. all_results = tuple( LintResults( itertools.chain.from_iterable( per_file_results.results for per_file_results in all_linter_results), linter_name=linter_name, ) for linter_name, all_linter_results in itertools.groupby( sorted_all_per_files_results, key=key_fn)) else: all_results = await MultiGet( Get(LintResults, LintRequest, lint_request) for lint_request in valid_requests) all_results = tuple( sorted(all_results, key=lambda results: results.linter_name)) reports = list( itertools.chain.from_iterable(results.reports for results in all_results)) if reports: # TODO(#10532): Tolerate when a linter has multiple reports. linters_with_multiple_reports = [ results.linter_name for results in all_results if len(results.reports) > 1 ] if linters_with_multiple_reports: if lint_subsystem.per_file_caching: suggestion = "Try running without `--lint-per-file-caching` set." else: suggestion = ( "The linters likely partitioned the input targets, such as grouping by Python " "interpreter compatibility. Try running on fewer targets or unset " "`--lint-reports-dir`.") raise InvalidLinterReportsError( "Multiple reports would have been written for these linters: " f"{linters_with_multiple_reports}. The option `--lint-reports-dir` only works if " f"each linter has a single result. {suggestion}") merged_reports = await Get( Digest, MergeDigests(report.digest for report in reports)) workspace.write_digest(merged_reports) logger.info( f"Wrote lint result files to {lint_subsystem.reports_dir}.") exit_code = 0 if all_results: console.print_stderr("") for results in all_results: 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.linter_name} {status}.") return Lint(exit_code)