diff --git a/src/python/pants/backend/python/lint/bandit/rules.py b/src/python/pants/backend/python/lint/bandit/rules.py index 80e65422ede..2beb5766254 100644 --- a/src/python/pants/backend/python/lint/bandit/rules.py +++ b/src/python/pants/backend/python/lint/bandit/rules.py @@ -15,7 +15,7 @@ from pants.backend.python.subsystems import python_native_code, subprocess_environment from pants.backend.python.subsystems.subprocess_environment import SubprocessEncodingEnvironment from pants.backend.python.target_types import PythonInterpreterCompatibility, PythonSources -from pants.core.goals.lint import LinterFieldSet, LinterFieldSets, LintResult +from pants.core.goals.lint import LintRequest, LintResult from pants.core.util_rules import determine_source_files, strip_source_roots from pants.core.util_rules.determine_source_files import ( AllSourceFilesRequest, @@ -26,6 +26,7 @@ from pants.engine.process import FallibleProcessResult, Process from pants.engine.rules import SubsystemRule, named_rule from pants.engine.selectors import Get, MultiGet +from pants.engine.target import FieldSetWithOrigin from pants.engine.unions import UnionRule from pants.option.global_options import GlobMatchErrorBehavior from pants.python.python_setup import PythonSetup @@ -33,14 +34,14 @@ @dataclass(frozen=True) -class BanditFieldSet(LinterFieldSet): +class BanditFieldSet(FieldSetWithOrigin): required_fields = (PythonSources,) sources: PythonSources compatibility: PythonInterpreterCompatibility -class BanditFieldSets(LinterFieldSets): +class BanditRequest(LintRequest): field_set_type = BanditFieldSet @@ -55,7 +56,7 @@ def generate_args(*, specified_source_files: SourceFiles, bandit: Bandit) -> Tup @named_rule(desc="Lint using Bandit") async def bandit_lint( - field_sets: BanditFieldSets, + request: BanditRequest, bandit: Bandit, python_setup: PythonSetup, subprocess_encoding_environment: SubprocessEncodingEnvironment, @@ -66,7 +67,7 @@ async def bandit_lint( # NB: Bandit output depends upon which Python interpreter version it's run with. See # https://github.com/PyCQA/bandit#under-which-version-of-python-should-i-install-bandit. interpreter_constraints = PexInterpreterConstraints.create_from_compatibility_fields( - (field_set.compatibility for field_set in field_sets), python_setup=python_setup + (field_set.compatibility for field_set in request.field_sets), python_setup=python_setup ) or PexInterpreterConstraints(bandit.default_interpreter_constraints) requirements_pex_request = Get[Pex]( PexRequest( @@ -87,11 +88,11 @@ async def bandit_lint( ) all_source_files_request = Get[SourceFiles]( - AllSourceFilesRequest(field_set.sources for field_set in field_sets) + AllSourceFilesRequest(field_set.sources for field_set in request.field_sets) ) specified_source_files_request = Get[SourceFiles]( SpecifiedSourceFilesRequest( - (field_set.sources, field_set.origin) for field_set in field_sets + (field_set.sources, field_set.origin) for field_set in request.field_sets ) ) @@ -114,7 +115,7 @@ async def bandit_lint( ) address_references = ", ".join( - sorted(field_set.address.reference() for field_set in field_sets) + sorted(field_set.address.reference() for field_set in request.field_sets) ) process = requirements_pex.create_process( @@ -123,7 +124,7 @@ async def bandit_lint( pex_path="./bandit.pex", pex_args=generate_args(specified_source_files=specified_source_files, bandit=bandit), input_digest=input_digest, - description=f"Run Bandit on {pluralize(len(field_sets), 'target')}: {address_references}.", + description=f"Run Bandit on {pluralize(len(request.field_sets), 'target')}: {address_references}.", ) result = await Get[FallibleProcessResult](Process, process) return LintResult.from_fallible_process_result(result, linter_name="Bandit") @@ -133,7 +134,7 @@ def rules(): return [ bandit_lint, SubsystemRule(Bandit), - UnionRule(LinterFieldSets, BanditFieldSets), + UnionRule(LintRequest, BanditRequest), *download_pex_bin.rules(), *determine_source_files.rules(), *pex.rules(), diff --git a/src/python/pants/backend/python/lint/bandit/rules_integration_test.py b/src/python/pants/backend/python/lint/bandit/rules_integration_test.py index f0dc450053f..6e2ff02e588 100644 --- a/src/python/pants/backend/python/lint/bandit/rules_integration_test.py +++ b/src/python/pants/backend/python/lint/bandit/rules_integration_test.py @@ -3,7 +3,7 @@ from typing import List, Optional -from pants.backend.python.lint.bandit.rules import BanditFieldSet, BanditFieldSets +from pants.backend.python.lint.bandit.rules import BanditFieldSet, BanditRequest from pants.backend.python.lint.bandit.rules import rules as bandit_rules from pants.backend.python.target_types import PythonInterpreterCompatibility, PythonLibrary from pants.base.specs import FilesystemLiteralSpec, OriginSpec, SingleAddress @@ -27,7 +27,7 @@ class BanditIntegrationTest(ExternalToolTestBase): @classmethod def rules(cls): - return (*super().rules(), *bandit_rules(), RootRule(BanditFieldSets)) + return (*super().rules(), *bandit_rules(), RootRule(BanditRequest)) def make_target_with_origin( self, @@ -68,7 +68,7 @@ def run_bandit( return self.request_single_product( LintResult, Params( - BanditFieldSets(BanditFieldSet.create(tgt) for tgt in targets), + BanditRequest(BanditFieldSet.create(tgt) for tgt in targets), create_options_bootstrapper(args=args), ), ) diff --git a/src/python/pants/backend/python/lint/black/rules.py b/src/python/pants/backend/python/lint/black/rules.py index 6d6e557c8e4..9631c53ed0f 100644 --- a/src/python/pants/backend/python/lint/black/rules.py +++ b/src/python/pants/backend/python/lint/black/rules.py @@ -7,7 +7,7 @@ from typing import List, Optional, Tuple, Union, cast from pants.backend.python.lint.black.subsystem import Black -from pants.backend.python.lint.python_fmt import PythonFmtFieldSets +from pants.backend.python.lint.python_fmt import PythonFmtRequest from pants.backend.python.rules import download_pex_bin, pex from pants.backend.python.rules.pex import ( Pex, @@ -18,8 +18,8 @@ from pants.backend.python.subsystems import python_native_code, subprocess_environment from pants.backend.python.subsystems.subprocess_environment import SubprocessEncodingEnvironment from pants.backend.python.target_types import PythonSources -from pants.core.goals.fmt import FmtFieldSet, FmtFieldSets, FmtResult -from pants.core.goals.lint import LinterFieldSets, LintResult +from pants.core.goals.fmt import FmtRequest, FmtResult +from pants.core.goals.lint import LintRequest, LintResult from pants.core.util_rules import determine_source_files, strip_source_roots from pants.core.util_rules.determine_source_files import ( AllSourceFilesRequest, @@ -30,6 +30,7 @@ from pants.engine.process import FallibleProcessResult, Process, ProcessResult from pants.engine.rules import SubsystemRule, named_rule, rule from pants.engine.selectors import Get, MultiGet +from pants.engine.target import FieldSetWithOrigin from pants.engine.unions import UnionRule from pants.option.global_options import GlobMatchErrorBehavior from pants.python.python_setup import PythonSetup @@ -37,19 +38,19 @@ @dataclass(frozen=True) -class BlackFieldSet(FmtFieldSet): +class BlackFieldSet(FieldSetWithOrigin): required_fields = (PythonSources,) sources: PythonSources -class BlackFieldSets(FmtFieldSets): +class BlackRequest(FmtRequest, LintRequest): field_set_type = BlackFieldSet @dataclass(frozen=True) class SetupRequest: - field_sets: BlackFieldSets + request: BlackRequest check_only: bool @@ -106,11 +107,11 @@ async def setup( ) all_source_files_request = Get[SourceFiles]( - AllSourceFilesRequest(field_set.sources for field_set in request.field_sets) + AllSourceFilesRequest(field_set.sources for field_set in request.request.field_sets) ) specified_source_files_request = Get[SourceFiles]( SpecifiedSourceFilesRequest( - (field_set.sources, field_set.origin) for field_set in request.field_sets + (field_set.sources, field_set.origin) for field_set in request.request.field_sets ) ) @@ -119,7 +120,7 @@ async def setup( config_snapshot_request, specified_source_files_request, ] - if request.field_sets.prior_formatter_result is None: + if request.request.prior_formatter_result is None: requests.append(all_source_files_request) requirements_pex, config_snapshot, specified_source_files, *rest = cast( Union[Tuple[Pex, Snapshot, SourceFiles], Tuple[Pex, Snapshot, SourceFiles, SourceFiles]], @@ -127,8 +128,8 @@ async def setup( ) all_source_files_snapshot = ( - request.field_sets.prior_formatter_result - if request.field_sets.prior_formatter_result + request.request.prior_formatter_result + if request.request.prior_formatter_result else rest[0].snapshot ) @@ -139,7 +140,7 @@ async def setup( ) address_references = ", ".join( - sorted(field_set.address.reference() for field_set in request.field_sets) + sorted(field_set.address.reference() for field_set in request.request.field_sets) ) process = requirements_pex.create_process( @@ -154,14 +155,14 @@ async def setup( input_digest=input_digest, output_files=all_source_files_snapshot.files, description=( - f"Run Black on {pluralize(len(request.field_sets), 'target')}: {address_references}." + f"Run Black on {pluralize(len(request.request.field_sets), 'target')}: {address_references}." ), ) return Setup(process, original_digest=all_source_files_snapshot.digest) @named_rule(desc="Format using Black") -async def black_fmt(field_sets: BlackFieldSets, black: Black) -> FmtResult: +async def black_fmt(field_sets: BlackRequest, black: Black) -> FmtResult: if black.options.skip: return FmtResult.noop() setup = await Get[Setup](SetupRequest(field_sets, check_only=False)) @@ -175,7 +176,7 @@ async def black_fmt(field_sets: BlackFieldSets, black: Black) -> FmtResult: @named_rule(desc="Lint using Black") -async def black_lint(field_sets: BlackFieldSets, black: Black) -> LintResult: +async def black_lint(field_sets: BlackRequest, black: Black) -> LintResult: if black.options.skip: return LintResult.noop() setup = await Get[Setup](SetupRequest(field_sets, check_only=True)) @@ -191,8 +192,8 @@ def rules(): black_fmt, black_lint, SubsystemRule(Black), - UnionRule(PythonFmtFieldSets, BlackFieldSets), - UnionRule(LinterFieldSets, BlackFieldSets), + UnionRule(PythonFmtRequest, BlackRequest), + UnionRule(LintRequest, BlackRequest), *download_pex_bin.rules(), *determine_source_files.rules(), *pex.rules(), diff --git a/src/python/pants/backend/python/lint/black/rules_integration_test.py b/src/python/pants/backend/python/lint/black/rules_integration_test.py index 0f0432dd4e0..2152f496871 100644 --- a/src/python/pants/backend/python/lint/black/rules_integration_test.py +++ b/src/python/pants/backend/python/lint/black/rules_integration_test.py @@ -3,7 +3,7 @@ from typing import List, Optional, Tuple -from pants.backend.python.lint.black.rules import BlackFieldSet, BlackFieldSets +from pants.backend.python.lint.black.rules import BlackFieldSet, BlackRequest from pants.backend.python.lint.black.rules import rules as black_rules from pants.backend.python.target_types import PythonLibrary from pants.base.specs import FilesystemLiteralSpec, OriginSpec, SingleAddress @@ -30,7 +30,7 @@ class BlackIntegrationTest(ExternalToolTestBase): @classmethod def rules(cls): - return (*super().rules(), *black_rules(), RootRule(BlackFieldSets)) + return (*super().rules(), *black_rules(), RootRule(BlackRequest)) def make_target_with_origin( self, source_files: List[FileContent], *, origin: Optional[OriginSpec] = None, @@ -61,7 +61,7 @@ def run_black( options_bootstrapper = create_options_bootstrapper(args=args) field_sets = [BlackFieldSet.create(tgt) for tgt in targets] lint_result = self.request_single_product( - LintResult, Params(BlackFieldSets(field_sets), options_bootstrapper) + LintResult, Params(BlackRequest(field_sets), options_bootstrapper) ) input_sources = self.request_single_product( SourceFiles, @@ -73,7 +73,7 @@ def run_black( fmt_result = self.request_single_product( FmtResult, Params( - BlackFieldSets(field_sets, prior_formatter_result=input_sources.snapshot), + BlackRequest(field_sets, prior_formatter_result=input_sources.snapshot), options_bootstrapper, ), ) diff --git a/src/python/pants/backend/python/lint/docformatter/rules.py b/src/python/pants/backend/python/lint/docformatter/rules.py index 1ee9fdd4106..f106cb1579c 100644 --- a/src/python/pants/backend/python/lint/docformatter/rules.py +++ b/src/python/pants/backend/python/lint/docformatter/rules.py @@ -5,7 +5,7 @@ from typing import List, Tuple, Union, cast from pants.backend.python.lint.docformatter.subsystem import Docformatter -from pants.backend.python.lint.python_fmt import PythonFmtFieldSets +from pants.backend.python.lint.python_fmt import PythonFmtRequest from pants.backend.python.rules import download_pex_bin, pex from pants.backend.python.rules.pex import ( Pex, @@ -16,8 +16,8 @@ from pants.backend.python.subsystems import python_native_code, subprocess_environment from pants.backend.python.subsystems.subprocess_environment import SubprocessEncodingEnvironment from pants.backend.python.target_types import PythonSources -from pants.core.goals.fmt import FmtFieldSet, FmtFieldSets, FmtResult -from pants.core.goals.lint import LinterFieldSets, LintResult +from pants.core.goals.fmt import FmtRequest, FmtResult +from pants.core.goals.lint import LintRequest, LintResult from pants.core.util_rules import determine_source_files, strip_source_roots from pants.core.util_rules.determine_source_files import ( AllSourceFilesRequest, @@ -28,25 +28,26 @@ from pants.engine.process import FallibleProcessResult, Process, ProcessResult from pants.engine.rules import SubsystemRule, named_rule, rule from pants.engine.selectors import Get, MultiGet +from pants.engine.target import FieldSetWithOrigin from pants.engine.unions import UnionRule from pants.python.python_setup import PythonSetup from pants.util.strutil import pluralize @dataclass(frozen=True) -class DocformatterFieldSet(FmtFieldSet): +class DocformatterFieldSet(FieldSetWithOrigin): required_fields = (PythonSources,) sources: PythonSources -class DocformatterFieldSets(FmtFieldSets): +class DocformatterRequest(FmtRequest, LintRequest): field_set_type = DocformatterFieldSet @dataclass(frozen=True) class SetupRequest: - field_sets: DocformatterFieldSets + request: DocformatterRequest check_only: bool @@ -85,16 +86,16 @@ async def setup( ) all_source_files_request = Get[SourceFiles]( - AllSourceFilesRequest(field_set.sources for field_set in request.field_sets) + AllSourceFilesRequest(field_set.sources for field_set in request.request.field_sets) ) specified_source_files_request = Get[SourceFiles]( SpecifiedSourceFilesRequest( - (field_set.sources, field_set.origin) for field_set in request.field_sets + (field_set.sources, field_set.origin) for field_set in request.request.field_sets ) ) requests: List[Get] = [requirements_pex_request, specified_source_files_request] - if request.field_sets.prior_formatter_result is None: + if request.request.prior_formatter_result is None: requests.append(all_source_files_request) requirements_pex, specified_source_files, *rest = cast( Union[Tuple[Pex, SourceFiles], Tuple[Pex, SourceFiles, SourceFiles]], @@ -102,8 +103,8 @@ async def setup( ) all_source_files_snapshot = ( - request.field_sets.prior_formatter_result - if request.field_sets.prior_formatter_result + request.request.prior_formatter_result + if request.request.prior_formatter_result else rest[0].snapshot ) @@ -112,7 +113,7 @@ async def setup( ) address_references = ", ".join( - sorted(field_set.address.reference() for field_set in request.field_sets) + sorted(field_set.address.reference() for field_set in request.request.field_sets) ) process = requirements_pex.create_process( @@ -127,7 +128,7 @@ async def setup( input_digest=input_digest, output_files=all_source_files_snapshot.files, description=( - f"Run Docformatter on {pluralize(len(request.field_sets), 'target')}: " + f"Run Docformatter on {pluralize(len(request.request.field_sets), 'target')}: " f"{address_references}." ), ) @@ -135,12 +136,10 @@ async def setup( @named_rule(desc="Format Python docstrings with docformatter") -async def docformatter_fmt( - field_sets: DocformatterFieldSets, docformatter: Docformatter -) -> FmtResult: +async def docformatter_fmt(request: DocformatterRequest, docformatter: Docformatter) -> FmtResult: if docformatter.options.skip: return FmtResult.noop() - setup = await Get[Setup](SetupRequest(field_sets, check_only=False)) + setup = await Get[Setup](SetupRequest(request, check_only=False)) result = await Get[ProcessResult](Process, setup.process) return FmtResult.from_process_result( result, original_digest=setup.original_digest, formatter_name="Docformatter" @@ -148,12 +147,10 @@ async def docformatter_fmt( @named_rule(desc="Lint Python docstrings with docformatter") -async def docformatter_lint( - field_sets: DocformatterFieldSets, docformatter: Docformatter -) -> LintResult: +async def docformatter_lint(request: DocformatterRequest, docformatter: Docformatter) -> LintResult: if docformatter.options.skip: return LintResult.noop() - setup = await Get[Setup](SetupRequest(field_sets, check_only=True)) + setup = await Get[Setup](SetupRequest(request, check_only=True)) result = await Get[FallibleProcessResult](Process, setup.process) return LintResult.from_fallible_process_result(result, linter_name="Docformatter") @@ -164,8 +161,8 @@ def rules(): docformatter_fmt, docformatter_lint, SubsystemRule(Docformatter), - UnionRule(PythonFmtFieldSets, DocformatterFieldSets), - UnionRule(LinterFieldSets, DocformatterFieldSets), + UnionRule(PythonFmtRequest, DocformatterRequest), + UnionRule(LintRequest, DocformatterRequest), *download_pex_bin.rules(), *determine_source_files.rules(), *pex.rules(), diff --git a/src/python/pants/backend/python/lint/docformatter/rules_integration_test.py b/src/python/pants/backend/python/lint/docformatter/rules_integration_test.py index 7ce68d31c55..61954a98039 100644 --- a/src/python/pants/backend/python/lint/docformatter/rules_integration_test.py +++ b/src/python/pants/backend/python/lint/docformatter/rules_integration_test.py @@ -3,7 +3,7 @@ from typing import List, Optional, Tuple -from pants.backend.python.lint.docformatter.rules import DocformatterFieldSet, DocformatterFieldSets +from pants.backend.python.lint.docformatter.rules import DocformatterFieldSet, DocformatterRequest from pants.backend.python.lint.docformatter.rules import rules as docformatter_rules from pants.backend.python.target_types import PythonLibrary from pants.base.specs import FilesystemLiteralSpec, OriginSpec, SingleAddress @@ -27,7 +27,7 @@ class DocformatterIntegrationTest(ExternalToolTestBase): @classmethod def rules(cls): - return (*super().rules(), *docformatter_rules(), RootRule(DocformatterFieldSets)) + return (*super().rules(), *docformatter_rules(), RootRule(DocformatterRequest)) def make_target_with_origin( self, source_files: List[FileContent], *, origin: Optional[OriginSpec] = None, @@ -54,7 +54,7 @@ def run_docformatter( options_bootstrapper = create_options_bootstrapper(args=args) field_sets = [DocformatterFieldSet.create(tgt) for tgt in targets] lint_result = self.request_single_product( - LintResult, Params(DocformatterFieldSets(field_sets), options_bootstrapper) + LintResult, Params(DocformatterRequest(field_sets), options_bootstrapper) ) input_sources = self.request_single_product( SourceFiles, @@ -66,7 +66,7 @@ def run_docformatter( fmt_result = self.request_single_product( FmtResult, Params( - DocformatterFieldSets(field_sets, prior_formatter_result=input_sources.snapshot), + DocformatterRequest(field_sets, prior_formatter_result=input_sources.snapshot), options_bootstrapper, ), ) diff --git a/src/python/pants/backend/python/lint/flake8/rules.py b/src/python/pants/backend/python/lint/flake8/rules.py index b6e75087e0e..e7596b61e5f 100644 --- a/src/python/pants/backend/python/lint/flake8/rules.py +++ b/src/python/pants/backend/python/lint/flake8/rules.py @@ -15,7 +15,7 @@ from pants.backend.python.subsystems import python_native_code, subprocess_environment from pants.backend.python.subsystems.subprocess_environment import SubprocessEncodingEnvironment from pants.backend.python.target_types import PythonInterpreterCompatibility, PythonSources -from pants.core.goals.lint import LinterFieldSet, LinterFieldSets, LintResult +from pants.core.goals.lint import LintRequest, LintResult from pants.core.util_rules import determine_source_files, strip_source_roots from pants.core.util_rules.determine_source_files import ( AllSourceFilesRequest, @@ -26,6 +26,7 @@ from pants.engine.process import FallibleProcessResult, Process from pants.engine.rules import SubsystemRule, named_rule from pants.engine.selectors import Get, MultiGet +from pants.engine.target import FieldSetWithOrigin from pants.engine.unions import UnionRule from pants.option.global_options import GlobMatchErrorBehavior from pants.python.python_setup import PythonSetup @@ -33,14 +34,14 @@ @dataclass(frozen=True) -class Flake8FieldSet(LinterFieldSet): +class Flake8FieldSet(FieldSetWithOrigin): required_fields = (PythonSources,) sources: PythonSources compatibility: PythonInterpreterCompatibility -class Flake8FieldSets(LinterFieldSets): +class Flake8Request(LintRequest): field_set_type = Flake8FieldSet @@ -55,7 +56,7 @@ def generate_args(*, specified_source_files: SourceFiles, flake8: Flake8) -> Tup @named_rule(desc="Lint using Flake8") async def flake8_lint( - field_sets: Flake8FieldSets, + request: Flake8Request, flake8: Flake8, python_setup: PythonSetup, subprocess_encoding_environment: SubprocessEncodingEnvironment, @@ -67,7 +68,7 @@ async def flake8_lint( # each target runs with its own interpreter constraints. See # http://flake8.pycqa.org/en/latest/user/invocation.html. interpreter_constraints = PexInterpreterConstraints.create_from_compatibility_fields( - (field_set.compatibility for field_set in field_sets), python_setup + (field_set.compatibility for field_set in request.field_sets), python_setup ) or PexInterpreterConstraints(flake8.default_interpreter_constraints) requirements_pex_request = Get[Pex]( PexRequest( @@ -88,11 +89,11 @@ async def flake8_lint( ) all_source_files_request = Get[SourceFiles]( - AllSourceFilesRequest(field_set.sources for field_set in field_sets) + AllSourceFilesRequest(field_set.sources for field_set in request.field_sets) ) specified_source_files_request = Get[SourceFiles]( SpecifiedSourceFilesRequest( - (field_set.sources, field_set.origin) for field_set in field_sets + (field_set.sources, field_set.origin) for field_set in request.field_sets ) ) @@ -115,7 +116,7 @@ async def flake8_lint( ) address_references = ", ".join( - sorted(field_set.address.reference() for field_set in field_sets) + sorted(field_set.address.reference() for field_set in request.field_sets) ) process = requirements_pex.create_process( @@ -124,7 +125,7 @@ async def flake8_lint( pex_path="./flake8.pex", pex_args=generate_args(specified_source_files=specified_source_files, flake8=flake8), input_digest=input_digest, - description=f"Run Flake8 on {pluralize(len(field_sets), 'target')}: {address_references}.", + description=f"Run Flake8 on {pluralize(len(request.field_sets), 'target')}: {address_references}.", ) result = await Get[FallibleProcessResult](Process, process) return LintResult.from_fallible_process_result(result, linter_name="Flake8") @@ -134,7 +135,7 @@ def rules(): return [ flake8_lint, SubsystemRule(Flake8), - UnionRule(LinterFieldSets, Flake8FieldSets), + UnionRule(LintRequest, Flake8Request), *download_pex_bin.rules(), *determine_source_files.rules(), *pex.rules(), diff --git a/src/python/pants/backend/python/lint/flake8/rules_integration_test.py b/src/python/pants/backend/python/lint/flake8/rules_integration_test.py index 98c70688139..975befcd0ad 100644 --- a/src/python/pants/backend/python/lint/flake8/rules_integration_test.py +++ b/src/python/pants/backend/python/lint/flake8/rules_integration_test.py @@ -3,7 +3,7 @@ from typing import List, Optional -from pants.backend.python.lint.flake8.rules import Flake8FieldSet, Flake8FieldSets +from pants.backend.python.lint.flake8.rules import Flake8FieldSet, Flake8Request from pants.backend.python.lint.flake8.rules import rules as flake8_rules from pants.backend.python.target_types import PythonInterpreterCompatibility, PythonLibrary from pants.base.specs import FilesystemLiteralSpec, OriginSpec, SingleAddress @@ -26,7 +26,7 @@ class Flake8IntegrationTest(ExternalToolTestBase): @classmethod def rules(cls): - return (*super().rules(), *flake8_rules(), RootRule(Flake8FieldSets)) + return (*super().rules(), *flake8_rules(), RootRule(Flake8Request)) def make_target_with_origin( self, @@ -67,7 +67,7 @@ def run_flake8( return self.request_single_product( LintResult, Params( - Flake8FieldSets(Flake8FieldSet.create(tgt) for tgt in targets), + Flake8Request(Flake8FieldSet.create(tgt) for tgt in targets), create_options_bootstrapper(args=args), ), ) diff --git a/src/python/pants/backend/python/lint/isort/rules.py b/src/python/pants/backend/python/lint/isort/rules.py index d7eb69b8114..1861231624e 100644 --- a/src/python/pants/backend/python/lint/isort/rules.py +++ b/src/python/pants/backend/python/lint/isort/rules.py @@ -5,7 +5,7 @@ from typing import List, Optional, Tuple, Union, cast from pants.backend.python.lint.isort.subsystem import Isort -from pants.backend.python.lint.python_fmt import PythonFmtFieldSets +from pants.backend.python.lint.python_fmt import PythonFmtRequest from pants.backend.python.rules import download_pex_bin, pex from pants.backend.python.rules.pex import ( Pex, @@ -16,8 +16,8 @@ from pants.backend.python.subsystems import python_native_code, subprocess_environment from pants.backend.python.subsystems.subprocess_environment import SubprocessEncodingEnvironment from pants.backend.python.target_types import PythonSources -from pants.core.goals.fmt import FmtFieldSet, FmtFieldSets, FmtResult -from pants.core.goals.lint import LinterFieldSets, LintResult +from pants.core.goals.fmt import FmtRequest, FmtResult +from pants.core.goals.lint import LintRequest, LintResult from pants.core.util_rules import determine_source_files, strip_source_roots from pants.core.util_rules.determine_source_files import ( AllSourceFilesRequest, @@ -28,6 +28,7 @@ from pants.engine.process import FallibleProcessResult, Process, ProcessResult from pants.engine.rules import SubsystemRule, named_rule, rule from pants.engine.selectors import Get, MultiGet +from pants.engine.target import FieldSetWithOrigin from pants.engine.unions import UnionRule from pants.option.custom_types import GlobExpansionConjunction from pants.option.global_options import GlobMatchErrorBehavior @@ -36,19 +37,19 @@ @dataclass(frozen=True) -class IsortFieldSet(FmtFieldSet): +class IsortFieldSet(FieldSetWithOrigin): required_fields = (PythonSources,) sources: PythonSources -class IsortFieldSets(FmtFieldSets): +class IsortRequest(FmtRequest, LintRequest): field_set_type = IsortFieldSet @dataclass(frozen=True) class SetupRequest: - field_sets: IsortFieldSets + request: IsortRequest check_only: bool @@ -100,11 +101,11 @@ async def setup( ) all_source_files_request = Get[SourceFiles]( - AllSourceFilesRequest(field_set.sources for field_set in request.field_sets) + AllSourceFilesRequest(field_set.sources for field_set in request.request.field_sets) ) specified_source_files_request = Get[SourceFiles]( SpecifiedSourceFilesRequest( - (field_set.sources, field_set.origin) for field_set in request.field_sets + (field_set.sources, field_set.origin) for field_set in request.request.field_sets ) ) @@ -113,7 +114,7 @@ async def setup( config_snapshot_request, specified_source_files_request, ] - if request.field_sets.prior_formatter_result is None: + if request.request.prior_formatter_result is None: requests.append(all_source_files_request) requirements_pex, config_snapshot, specified_source_files, *rest = cast( Union[Tuple[Pex, Snapshot, SourceFiles], Tuple[Pex, Snapshot, SourceFiles, SourceFiles]], @@ -121,8 +122,8 @@ async def setup( ) all_source_files_snapshot = ( - request.field_sets.prior_formatter_result - if request.field_sets.prior_formatter_result + request.request.prior_formatter_result + if request.request.prior_formatter_result else rest[0].snapshot ) @@ -133,7 +134,7 @@ async def setup( ) address_references = ", ".join( - sorted(field_set.address.reference() for field_set in request.field_sets) + sorted(field_set.address.reference() for field_set in request.request.field_sets) ) process = requirements_pex.create_process( @@ -148,17 +149,17 @@ async def setup( input_digest=input_digest, output_files=all_source_files_snapshot.files, description=( - f"Run isort on {pluralize(len(request.field_sets), 'target')}: {address_references}." + f"Run isort on {pluralize(len(request.request.field_sets), 'target')}: {address_references}." ), ) return Setup(process, original_digest=all_source_files_snapshot.digest) @named_rule(desc="Format using isort") -async def isort_fmt(field_sets: IsortFieldSets, isort: Isort) -> FmtResult: +async def isort_fmt(request: IsortRequest, isort: Isort) -> FmtResult: if isort.options.skip: return FmtResult.noop() - setup = await Get[Setup](SetupRequest(field_sets, check_only=False)) + setup = await Get[Setup](SetupRequest(request, check_only=False)) result = await Get[ProcessResult](Process, setup.process) return FmtResult.from_process_result( result, @@ -169,10 +170,10 @@ async def isort_fmt(field_sets: IsortFieldSets, isort: Isort) -> FmtResult: @named_rule(desc="Lint using isort") -async def isort_lint(field_sets: IsortFieldSets, isort: Isort) -> LintResult: +async def isort_lint(request: IsortRequest, isort: Isort) -> LintResult: if isort.options.skip: return LintResult.noop() - setup = await Get[Setup](SetupRequest(field_sets, check_only=True)) + setup = await Get[Setup](SetupRequest(request, check_only=True)) result = await Get[FallibleProcessResult](Process, setup.process) return LintResult.from_fallible_process_result( result, linter_name="isort", strip_chroot_path=True @@ -185,8 +186,8 @@ def rules(): isort_fmt, isort_lint, SubsystemRule(Isort), - UnionRule(PythonFmtFieldSets, IsortFieldSets), - UnionRule(LinterFieldSets, IsortFieldSets), + UnionRule(PythonFmtRequest, IsortRequest), + UnionRule(LintRequest, IsortRequest), *download_pex_bin.rules(), *determine_source_files.rules(), *pex.rules(), diff --git a/src/python/pants/backend/python/lint/isort/rules_integration_test.py b/src/python/pants/backend/python/lint/isort/rules_integration_test.py index ae52be4c9fe..abff969893e 100644 --- a/src/python/pants/backend/python/lint/isort/rules_integration_test.py +++ b/src/python/pants/backend/python/lint/isort/rules_integration_test.py @@ -3,7 +3,7 @@ from typing import List, Optional, Tuple -from pants.backend.python.lint.isort.rules import IsortFieldSet, IsortFieldSets +from pants.backend.python.lint.isort.rules import IsortFieldSet, IsortRequest from pants.backend.python.lint.isort.rules import rules as isort_rules from pants.backend.python.target_types import PythonLibrary from pants.base.specs import FilesystemLiteralSpec, OriginSpec, SingleAddress @@ -37,7 +37,7 @@ class IsortIntegrationTest(ExternalToolTestBase): @classmethod def rules(cls): - return (*super().rules(), *isort_rules(), RootRule(IsortFieldSets)) + return (*super().rules(), *isort_rules(), RootRule(IsortRequest)) def make_target_with_origin( self, source_files: List[FileContent], *, origin: Optional[OriginSpec] = None, @@ -68,7 +68,7 @@ def run_isort( options_bootstrapper = create_options_bootstrapper(args=args) field_sets = [IsortFieldSet.create(tgt) for tgt in targets] lint_result = self.request_single_product( - LintResult, Params(IsortFieldSets(field_sets), options_bootstrapper) + LintResult, Params(IsortRequest(field_sets), options_bootstrapper) ) input_sources = self.request_single_product( SourceFiles, @@ -80,7 +80,7 @@ def run_isort( fmt_result = self.request_single_product( FmtResult, Params( - IsortFieldSets(field_sets, prior_formatter_result=input_sources.snapshot), + IsortRequest(field_sets, prior_formatter_result=input_sources.snapshot), options_bootstrapper, ), ) diff --git a/src/python/pants/backend/python/lint/pylint/rules.py b/src/python/pants/backend/python/lint/pylint/rules.py index f83e7f35251..535989dc5f7 100644 --- a/src/python/pants/backend/python/lint/pylint/rules.py +++ b/src/python/pants/backend/python/lint/pylint/rules.py @@ -21,7 +21,7 @@ PythonRequirementsField, PythonSources, ) -from pants.core.goals.lint import LinterFieldSet, LinterFieldSets, LintResult +from pants.core.goals.lint import LintRequest, LintResult from pants.core.util_rules import determine_source_files, strip_source_roots from pants.core.util_rules.determine_source_files import SourceFiles, SpecifiedSourceFilesRequest from pants.engine.addresses import Address, Addresses @@ -29,7 +29,13 @@ from pants.engine.process import FallibleProcessResult, Process from pants.engine.rules import SubsystemRule, named_rule from pants.engine.selectors import Get, MultiGet -from pants.engine.target import Dependencies, DependenciesRequest, Targets, TransitiveTargets +from pants.engine.target import ( + Dependencies, + DependenciesRequest, + FieldSetWithOrigin, + Targets, + TransitiveTargets, +) from pants.engine.unions import UnionRule from pants.option.global_options import GlobMatchErrorBehavior from pants.python.python_setup import PythonSetup @@ -37,14 +43,14 @@ @dataclass(frozen=True) -class PylintFieldSet(LinterFieldSet): +class PylintFieldSet(FieldSetWithOrigin): required_fields = (PythonSources,) sources: PythonSources dependencies: Dependencies -class PylintFieldSets(LinterFieldSets): +class PylintRequest(LintRequest): field_set_type = PylintFieldSet @@ -59,7 +65,7 @@ def generate_args(*, specified_source_files: SourceFiles, pylint: Pylint) -> Tup @named_rule(desc="Lint using Pylint") async def pylint_lint( - field_sets: PylintFieldSets, + request: PylintRequest, pylint: Pylint, python_setup: PythonSetup, subprocess_encoding_environment: SubprocessEncodingEnvironment, @@ -70,13 +76,16 @@ async def pylint_lint( # Pylint needs direct dependencies in the chroot to ensure that imports are valid. However, it # doesn't lint those direct dependencies nor does it care about transitive dependencies. per_target_dependencies = await MultiGet( - Get[Targets](DependenciesRequest(field_set.dependencies)) for field_set in field_sets + Get[Targets](DependenciesRequest(field_set.dependencies)) + for field_set in request.field_sets ) plugin_targets_request = Get[TransitiveTargets]( Addresses(Address.parse(plugin_addr) for plugin_addr in pylint.source_plugins) ) - linted_targets_request = Get[Targets](Addresses(field_set.address for field_set in field_sets)) + linted_targets_request = Get[Targets]( + Addresses(field_set.address for field_set in request.field_sets) + ) plugin_targets, linted_targets = cast( Tuple[TransitiveTargets, Targets], await MultiGet([plugin_targets_request, linted_targets_request]), @@ -162,7 +171,7 @@ async def pylint_lint( ) specified_source_files_request = Get[SourceFiles]( SpecifiedSourceFilesRequest( - ((field_set.sources, field_set.origin) for field_set in field_sets), + ((field_set.sources, field_set.origin) for field_set in request.field_sets), strip_source_roots=True, ) ) @@ -212,7 +221,7 @@ async def pylint_lint( ) address_references = ", ".join( - sorted(field_set.address.reference() for field_set in field_sets) + sorted(field_set.address.reference() for field_set in request.field_sets) ) process = pylint_runner_pex.create_process( @@ -227,7 +236,7 @@ async def pylint_lint( env={"PYTHONPATH": "./__plugins"} if pylint.source_plugins else None, pex_args=generate_args(specified_source_files=specified_source_files, pylint=pylint), input_digest=input_digest, - description=f"Run Pylint on {pluralize(len(field_sets), 'target')}: {address_references}.", + description=f"Run Pylint on {pluralize(len(request.field_sets), 'target')}: {address_references}.", ) result = await Get[FallibleProcessResult](Process, process) return LintResult.from_fallible_process_result(result, linter_name="Pylint") @@ -237,7 +246,7 @@ def rules(): return [ pylint_lint, SubsystemRule(Pylint), - UnionRule(LinterFieldSets, PylintFieldSets), + UnionRule(LintRequest, PylintRequest), *download_pex_bin.rules(), *determine_source_files.rules(), *pex.rules(), diff --git a/src/python/pants/backend/python/lint/pylint/rules_integration_test.py b/src/python/pants/backend/python/lint/pylint/rules_integration_test.py index 88f16adafd0..a210b70529f 100644 --- a/src/python/pants/backend/python/lint/pylint/rules_integration_test.py +++ b/src/python/pants/backend/python/lint/pylint/rules_integration_test.py @@ -6,7 +6,7 @@ from typing import List, Optional from pants.backend.python.lint.pylint.plugin_target_type import PylintSourcePlugin -from pants.backend.python.lint.pylint.rules import PylintFieldSet, PylintFieldSets +from pants.backend.python.lint.pylint.rules import PylintFieldSet, PylintRequest from pants.backend.python.lint.pylint.rules import rules as pylint_rules from pants.backend.python.target_types import PythonLibrary, PythonRequirementLibrary from pants.base.specs import FilesystemLiteralSpec, OriginSpec, SingleAddress @@ -52,7 +52,7 @@ def rules(cls): return ( *super().rules(), *pylint_rules(), - RootRule(PylintFieldSets), + RootRule(PylintRequest), RootRule(HydratedTargets), ) @@ -108,7 +108,7 @@ def run_pylint( return self.request_single_product( LintResult, Params( - PylintFieldSets(PylintFieldSet.create(tgt) for tgt in targets), + PylintRequest(PylintFieldSet.create(tgt) for tgt in targets), create_options_bootstrapper(args=args), ), ) diff --git a/src/python/pants/backend/python/lint/python_fmt.py b/src/python/pants/backend/python/lint/python_fmt.py index bea91bb5f21..0d4c1fb2de8 100644 --- a/src/python/pants/backend/python/lint/python_fmt.py +++ b/src/python/pants/backend/python/lint/python_fmt.py @@ -5,7 +5,7 @@ from typing import Iterable, List, Type from pants.backend.python.target_types import PythonSources -from pants.core.goals.fmt import FmtFieldSets, FmtResult, LanguageFmtResults, LanguageFmtTargets +from pants.core.goals.fmt import FmtRequest, FmtResult, LanguageFmtResults, LanguageFmtTargets from pants.core.util_rules.determine_source_files import AllSourceFilesRequest, SourceFiles from pants.engine.fs import Digest, Snapshot from pants.engine.rules import rule @@ -19,7 +19,7 @@ class PythonFmtTargets(LanguageFmtTargets): @union -class PythonFmtFieldSets: +class PythonFmtRequest(FmtRequest): pass @@ -37,15 +37,13 @@ async def format_python_target( prior_formatter_result = original_sources.snapshot results: List[FmtResult] = [] - field_set_collection_types: Iterable[Type[FmtFieldSets]] = union_membership.union_rules[ - PythonFmtFieldSets - ] - for field_set_collection_type in field_set_collection_types: + fmt_request_types: Iterable[Type[FmtRequest]] = union_membership.union_rules[PythonFmtRequest] + for fmt_request_type in fmt_request_types: result = await Get[FmtResult]( - PythonFmtFieldSets, - field_set_collection_type( + PythonFmtRequest, + fmt_request_type( ( - field_set_collection_type.field_set_type.create(target_with_origin) + fmt_request_type.field_set_type.create(target_with_origin) for target_with_origin in targets_with_origins ), prior_formatter_result=prior_formatter_result, diff --git a/src/python/pants/backend/python/lint/python_fmt_integration_test.py b/src/python/pants/backend/python/lint/python_fmt_integration_test.py index ef26683ccda..941ce11bc35 100644 --- a/src/python/pants/backend/python/lint/python_fmt_integration_test.py +++ b/src/python/pants/backend/python/lint/python_fmt_integration_test.py @@ -3,9 +3,9 @@ from typing import List, Optional -from pants.backend.python.lint.black.rules import BlackFieldSets +from pants.backend.python.lint.black.rules import BlackRequest from pants.backend.python.lint.black.rules import rules as black_rules -from pants.backend.python.lint.isort.rules import IsortFieldSets +from pants.backend.python.lint.isort.rules import IsortRequest from pants.backend.python.lint.isort.rules import rules as isort_rules from pants.backend.python.lint.python_fmt import PythonFmtTargets, format_python_target from pants.backend.python.target_types import PythonLibrary @@ -29,8 +29,8 @@ def rules(cls): *black_rules(), *isort_rules(), RootRule(PythonFmtTargets), - RootRule(BlackFieldSets), - RootRule(IsortFieldSets), + RootRule(BlackRequest), + RootRule(IsortRequest), ) def run_black_and_isort( @@ -93,7 +93,7 @@ def test_skipped_formatter(self) -> None: def test_no_changes(self) -> None: source = FileContent( - "test/skipped.py", content=b"from animals import dog, cat\n\nprint('hello')\n", + "test/target.py", content=b'from animals import cat, dog\n\nprint("hello")\n', ) results = self.run_black_and_isort([source], name="different_file") assert results.output == self.get_digest([source]) diff --git a/src/python/pants/core/goals/BUILD b/src/python/pants/core/goals/BUILD index a4e8f82c1b2..7be9985044c 100644 --- a/src/python/pants/core/goals/BUILD +++ b/src/python/pants/core/goals/BUILD @@ -55,6 +55,7 @@ python_tests( sources=['*_integration_test.py'], dependencies=[ ':goals', + 'examples/src/python/example:hello_directory', 'src/python/pants/backend/python/rules', 'src/python/pants/backend/python/subsystems', 'src/python/pants/build_graph', @@ -63,6 +64,10 @@ python_tests( 'src/python/pants/engine:interactive_runner', 'src/python/pants/engine:rules', 'src/python/pants/testutil:goal_rule_test_base', + 'src/python/pants/testutil:int-test', + '//:isort_cfg', + 'examples:isort_cfg', + 'contrib:isort_cfg', ], tags = {'integration', 'partially_type_checked'}, ) diff --git a/src/python/pants/core/goals/fmt.py b/src/python/pants/core/goals/fmt.py index a10305830d6..b6f8cc97add 100644 --- a/src/python/pants/core/goals/fmt.py +++ b/src/python/pants/core/goals/fmt.py @@ -2,22 +2,13 @@ # Licensed under the Apache License, Version 2.0 (see LICENSE). import itertools -from abc import ABCMeta from dataclasses import dataclass -from typing import ClassVar, Iterable, List, Optional, Tuple, Type +from typing import ClassVar, Iterable, List, Tuple, Type -from pants.core.goals.lint import LinterFieldSet +from pants.core.goals.style_request import StyleRequest from pants.core.util_rules.filter_empty_sources import TargetsWithSources, TargetsWithSourcesRequest -from pants.engine.collection import Collection from pants.engine.console import Console -from pants.engine.fs import ( - EMPTY_DIGEST, - Digest, - DirectoryToMaterialize, - MergeDigests, - Snapshot, - Workspace, -) +from pants.engine.fs import EMPTY_DIGEST, Digest, DirectoryToMaterialize, MergeDigests, Workspace from pants.engine.goal import Goal, GoalSubsystem from pants.engine.process import ProcessResult from pants.engine.rules import goal_rule @@ -65,24 +56,12 @@ def did_change(self) -> bool: return self.output != self.input -class FmtFieldSet(LinterFieldSet, metaclass=ABCMeta): - """The fields necessary for a particular auto-formatter to work with a target.""" - - -class FmtFieldSets(Collection[FmtFieldSet]): - """A collection of `FieldSet`s for a particular formatter, e.g. a collection of - `IsortFieldSet`s.""" - - field_set_type: ClassVar[Type[FmtFieldSet]] +@union +class FmtRequest(StyleRequest): + """A union for StyleRequests that should be formattable. - def __init__( - self, - field_sets: Iterable[FmtFieldSet], - *, - prior_formatter_result: Optional[Snapshot] = None, - ) -> None: - super().__init__(field_sets) - self.prior_formatter_result = prior_formatter_result + Subclass and install a member of this type to provide a formatter. + """ @union diff --git a/src/python/pants/core/goals/fmt_integration_test.py b/src/python/pants/core/goals/fmt_integration_test.py new file mode 100644 index 00000000000..bbd1bed9400 --- /dev/null +++ b/src/python/pants/core/goals/fmt_integration_test.py @@ -0,0 +1,31 @@ +# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +import re + +from pants.testutil.pants_run_integration_test import PantsRunIntegrationTest, ensure_daemon +from pants.util.dirutil import read_file + + +class FmtIntegrationTest(PantsRunIntegrationTest): + @ensure_daemon + def test_fmt_then_edit(self): + f = "examples/src/python/example/hello/greet/greet.py" + with self.temporary_workdir() as workdir: + run = lambda: self.run_pants_with_workdir( + ["--no-v1", "--v2", "fmt", f], workdir=workdir + ) + + # Run once to start up, and then capture the file content. + self.assert_success(run()) + good_content = read_file(f) + + # Edit the file. + with self.with_overwritten_file_content( + f, lambda c: re.sub(b"def greet", b"def greet", c) + ): + assert good_content != read_file(f) + + # Re-run and confirm that the file was fixed. + self.assert_success(run()) + assert good_content == read_file(f) diff --git a/src/python/pants/core/goals/lint.py b/src/python/pants/core/goals/lint.py index ac72f7625e0..d2e56a46c88 100644 --- a/src/python/pants/core/goals/lint.py +++ b/src/python/pants/core/goals/lint.py @@ -1,21 +1,20 @@ # Copyright 2019 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). -from abc import ABCMeta from dataclasses import dataclass -from typing import ClassVar, Iterable, Type, TypeVar +from typing import Iterable +from pants.core.goals.style_request import StyleRequest from pants.core.util_rules.filter_empty_sources import ( FieldSetsWithSources, FieldSetsWithSourcesRequest, ) -from pants.engine.collection import Collection from pants.engine.console import Console from pants.engine.goal import Goal, GoalSubsystem from pants.engine.process import FallibleProcessResult from pants.engine.rules import goal_rule from pants.engine.selectors import Get, MultiGet -from pants.engine.target import FieldSetWithOrigin, Sources, TargetsWithOrigins +from pants.engine.target import TargetsWithOrigins from pants.engine.unions import UnionMembership, union from pants.util.strutil import strip_v2_chroot_path @@ -46,21 +45,12 @@ def prep_output(s: bytes) -> str: ) -class LinterFieldSet(FieldSetWithOrigin, metaclass=ABCMeta): - """The fields necessary for a particular linter to work with a target.""" - - sources: Sources - - -_FS = TypeVar("_FS", bound="LinterFieldSet") - - @union -class LinterFieldSets(Collection[_FS]): - """A collection of `FieldSet`s for a particular linter, e.g. a collection of - `Flake8FieldSet`s.""" +class LintRequest(StyleRequest): + """A union for StyleRequests that should be lintable. - field_set_type: ClassVar[Type[_FS]] + Subclass and install a member of this type to provide a linter. + """ class LintOptions(GoalSubsystem): @@ -68,7 +58,7 @@ class LintOptions(GoalSubsystem): name = "lint" - required_union_implementations = (LinterFieldSets,) + required_union_implementations = (LintRequest,) @classmethod def register_options(cls, register) -> None: @@ -105,40 +95,34 @@ async def lint( options: LintOptions, union_membership: UnionMembership, ) -> Lint: - field_set_collection_types = union_membership[LinterFieldSets] - field_set_collections: Iterable[LinterFieldSets] = tuple( - field_set_collection_type( - field_set_collection_type.field_set_type.create(target_with_origin) + 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 field_set_collection_type.field_set_type.is_valid(target_with_origin.target) + if lint_request_type.field_set_type.is_valid(target_with_origin.target) ) - for field_set_collection_type in union_membership[LinterFieldSets] + for lint_request_type in union_membership[LintRequest] ) - field_set_collections_with_sources: Iterable[FieldSetsWithSources] = await MultiGet( - Get[FieldSetsWithSources](FieldSetsWithSourcesRequest(field_set_collection)) - for field_set_collection in field_set_collections + field_sets_with_sources: Iterable[FieldSetsWithSources] = await MultiGet( + Get[FieldSetsWithSources](FieldSetsWithSourcesRequest(lint_request.field_sets)) + for lint_request in lint_requests ) - # NB: We must convert back the generic FieldSetsWithSources objects back into their - # corresponding LinterFieldSets, e.g. back to IsortFieldSets, in order for the union rule to - # work. - valid_field_set_collections: Iterable[LinterFieldSets] = tuple( - field_set_collection_cls(field_set_collection) - for field_set_collection_cls, field_set_collection in zip( - field_set_collection_types, field_set_collections_with_sources - ) - if field_set_collection + 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[LintResult](LinterFieldSets, field_set_collection.__class__([field_set])) - for field_set_collection in valid_field_set_collections - for field_set in field_set_collection + Get[LintResult](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[LintResult](LinterFieldSets, field_set_collection) - for field_set_collection in valid_field_set_collections + Get[LintResult](LintRequest, lint_request) for lint_request in valid_lint_requests ) if not results: diff --git a/src/python/pants/core/goals/lint_test.py b/src/python/pants/core/goals/lint_test.py index 2a2b67327cf..ecc68936dda 100644 --- a/src/python/pants/core/goals/lint_test.py +++ b/src/python/pants/core/goals/lint_test.py @@ -6,20 +6,19 @@ from typing import ClassVar, Iterable, List, Optional, Tuple, Type from pants.base.specs import SingleAddress -from pants.core.goals.lint import ( - Lint, - LinterFieldSet, - LinterFieldSets, - LintOptions, - LintResult, - lint, -) +from pants.core.goals.lint import Lint, LintOptions, LintRequest, LintResult, lint from pants.core.util_rules.filter_empty_sources import ( FieldSetsWithSources, FieldSetsWithSourcesRequest, ) from pants.engine.addresses import Address -from pants.engine.target import Sources, Target, TargetsWithOrigins, TargetWithOrigin +from pants.engine.target import ( + FieldSetWithOrigin, + Sources, + Target, + TargetsWithOrigins, + TargetWithOrigin, +) from pants.engine.unions import UnionMembership from pants.testutil.engine.util import MockConsole, MockGet, create_goal_subsystem, run_rule from pants.testutil.test_base import TestBase @@ -30,11 +29,11 @@ class MockTarget(Target): core_fields = (Sources,) -class MockLinterFieldSet(LinterFieldSet): +class MockLinterFieldSet(FieldSetWithOrigin): required_fields = (Sources,) -class MockLinterFieldSets(LinterFieldSets, metaclass=ABCMeta): +class MockLintRequest(LintRequest, metaclass=ABCMeta): field_set_type = MockLinterFieldSet linter_name: ClassVar[str] @@ -50,13 +49,13 @@ def stdout(_: Iterable[Address]) -> str: @property def lint_result(self) -> LintResult: - addresses = [config.address for config in self] + addresses = [config.address for config in self.field_sets] return LintResult( self.exit_code(addresses), self.stdout(addresses), "", linter_name=self.linter_name ) -class SuccessfulFieldSets(MockLinterFieldSets): +class SuccessfulFieldSets(MockLintRequest): linter_name = "SuccessfulLinter" @staticmethod @@ -68,7 +67,7 @@ def stdout(addresses: Iterable[Address]) -> str: return ", ".join(str(address) for address in addresses) -class FailingFieldSets(MockLinterFieldSets): +class FailingFieldSets(MockLintRequest): linter_name = "FailingLinter" @staticmethod @@ -80,7 +79,7 @@ def stdout(addresses: Iterable[Address]) -> str: return ", ".join(str(address) for address in addresses) -class ConditionallySucceedsFieldSets(MockLinterFieldSets): +class ConditionallySucceedsFieldSets(MockLintRequest): linter_name = "ConditionallySucceedsLinter" @staticmethod @@ -102,7 +101,7 @@ class InvalidFieldSet(MockLinterFieldSet): required_fields = (InvalidField,) -class InvalidFieldSets(MockLinterFieldSets): +class InvalidFieldSets(MockLintRequest): field_set_type = InvalidFieldSet linter_name = "InvalidLinter" @@ -128,13 +127,13 @@ def make_target_with_origin(address: Optional[Address] = None) -> TargetWithOrig @staticmethod def run_lint_rule( *, - field_set_collection_types: List[Type[LinterFieldSets]], + lint_request_types: List[Type[LintRequest]], targets: List[TargetWithOrigin], per_target_caching: bool, include_sources: bool = True, ) -> Tuple[int, str]: console = MockConsole(use_colors=False) - union_membership = UnionMembership({LinterFieldSets: field_set_collection_types}) + union_membership = UnionMembership({LintRequest: lint_request_types}) result: Lint = run_rule( lint, rule_args=[ @@ -146,7 +145,7 @@ def run_lint_rule( mock_gets=[ MockGet( product_type=LintResult, - subject_type=LinterFieldSets, + subject_type=LintRequest, mock=lambda field_set_collection: field_set_collection.lint_result, ), MockGet( @@ -165,7 +164,7 @@ def run_lint_rule( def test_empty_target_noops(self) -> None: def assert_noops(per_target_caching: bool) -> None: exit_code, stderr = self.run_lint_rule( - field_set_collection_types=[FailingFieldSets], + lint_request_types=[FailingFieldSets], targets=[self.make_target_with_origin()], per_target_caching=per_target_caching, include_sources=False, @@ -179,7 +178,7 @@ def assert_noops(per_target_caching: bool) -> None: def test_invalid_target_noops(self) -> None: def assert_noops(per_target_caching: bool) -> None: exit_code, stderr = self.run_lint_rule( - field_set_collection_types=[InvalidFieldSets], + lint_request_types=[InvalidFieldSets], targets=[self.make_target_with_origin()], per_target_caching=per_target_caching, ) @@ -195,7 +194,7 @@ def test_single_target_with_one_linter(self) -> None: def assert_expected(per_target_caching: bool) -> None: exit_code, stderr = self.run_lint_rule( - field_set_collection_types=[FailingFieldSets], + lint_request_types=[FailingFieldSets], targets=[target_with_origin], per_target_caching=per_target_caching, ) @@ -216,7 +215,7 @@ def test_single_target_with_multiple_linters(self) -> None: def assert_expected(per_target_caching: bool) -> None: exit_code, stderr = self.run_lint_rule( - field_set_collection_types=[SuccessfulFieldSets, FailingFieldSets], + lint_request_types=[SuccessfulFieldSets, FailingFieldSets], targets=[target_with_origin], per_target_caching=per_target_caching, ) @@ -240,7 +239,7 @@ def test_multiple_targets_with_one_linter(self) -> None: def get_stderr(*, per_target_caching: bool) -> str: exit_code, stderr = self.run_lint_rule( - field_set_collection_types=[ConditionallySucceedsFieldSets], + lint_request_types=[ConditionallySucceedsFieldSets], targets=[ self.make_target_with_origin(good_address), self.make_target_with_origin(bad_address), @@ -273,7 +272,7 @@ def test_multiple_targets_with_multiple_linters(self) -> None: def get_stderr(*, per_target_caching: bool) -> str: exit_code, stderr = self.run_lint_rule( - field_set_collection_types=[ConditionallySucceedsFieldSets, SuccessfulFieldSets,], + lint_request_types=[ConditionallySucceedsFieldSets, SuccessfulFieldSets,], targets=[ self.make_target_with_origin(good_address), self.make_target_with_origin(bad_address), diff --git a/src/python/pants/core/goals/style_request.py b/src/python/pants/core/goals/style_request.py new file mode 100644 index 00000000000..64c64347c2b --- /dev/null +++ b/src/python/pants/core/goals/style_request.py @@ -0,0 +1,34 @@ +# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from abc import ABCMeta +from dataclasses import dataclass +from typing import ClassVar, Generic, Iterable, Optional, Type, TypeVar + +from pants.engine.collection import Collection +from pants.engine.fs import Snapshot +from pants.engine.target import FieldSetWithOrigin +from pants.util.meta import frozen_after_init + +_FS = TypeVar("_FS", bound=FieldSetWithOrigin) + + +@frozen_after_init +@dataclass(unsafe_hash=True) +class StyleRequest(Generic[_FS], metaclass=ABCMeta): + """A request to style a collection of `FieldSet`s. + + Should be subclassed for a particular style engine in order to support autoformatting or + linting. + """ + + field_set_type: ClassVar[Type[_FS]] + + field_sets: Collection[_FS] + prior_formatter_result: Optional[Snapshot] = None + + def __init__( + self, field_sets: Iterable[_FS], *, prior_formatter_result: Optional[Snapshot] = None, + ) -> None: + self.field_sets = Collection[_FS](field_sets) + self.prior_formatter_result = prior_formatter_result