async def docformatter_lint(configs: DocformatterConfigurations, docformatter: Docformatter) -> LintResult: if docformatter.options.skip: return LintResult.noop() setup = await Get[Setup](SetupRequest(configs, check_only=True)) result = await Get[FallibleProcessResult](Process, setup.process) return LintResult.from_fallible_process_result(result)
async def black_lint(field_sets: BlackFieldSets, black: Black) -> LintResult: if black.options.skip: return LintResult.noop() setup = await Get[Setup](SetupRequest(field_sets, check_only=True)) result = await Get[FallibleProcessResult](Process, setup.process) return LintResult.from_fallible_process_result(result, linter_name="Black", strip_chroot_path=True)
def test_precise_file_args(self) -> None: target = self.make_target_with_origin( [self.good_source, self.bad_source], origin=FilesystemLiteralSpec(self.good_source.path)) lint_result, fmt_result = self.run_docformatter([target]) assert lint_result == LintResult.noop() assert fmt_result.digest == self.get_digest( [self.good_source, self.bad_source])
async def isort_lint(field_sets: IsortFieldSets, isort: Isort) -> LintResult: if isort.options.skip: return LintResult.noop() setup = await Get[Setup](SetupRequest(field_sets, check_only=True)) result = await Get[FallibleProcessResult](Process, setup.process) return LintResult.from_fallible_process_result(result, linter_name="isort", strip_chroot_path=True)
async def docformatter_lint(field_sets: DocformatterFieldSets, docformatter: Docformatter) -> LintResult: if docformatter.options.skip: return LintResult.noop() setup = await Get[Setup](SetupRequest(field_sets, check_only=True)) result = await Get[FallibleProcessResult](Process, setup.process) return LintResult.from_fallible_process_result(result, linter_name="Docformatter")
async def bandit_lint( configs: BanditConfigurations, bandit: Bandit, python_setup: PythonSetup, subprocess_encoding_environment: SubprocessEncodingEnvironment, ) -> LintResult: if bandit.options.skip: return LintResult.noop() # 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( (config.compatibility for config in configs), python_setup=python_setup ) requirements_pex = await Get[Pex]( PexRequest( output_filename="bandit.pex", requirements=PexRequirements(bandit.get_requirement_specs()), interpreter_constraints=interpreter_constraints, entry_point=bandit.get_entry_point(), ) ) config_path: Optional[str] = bandit.options.config config_snapshot = await Get[Snapshot]( PathGlobs( globs=tuple([config_path] if config_path else []), glob_match_error_behavior=GlobMatchErrorBehavior.error, description_of_origin="the option `--bandit-config`", ) ) all_source_files = await Get[SourceFiles]( AllSourceFilesRequest(config.sources for config in configs) ) specified_source_files = await Get[SourceFiles]( SpecifiedSourceFilesRequest((config.sources, config.origin) for config in configs) ) merged_input_files = await Get[Digest]( MergeDigests( (all_source_files.snapshot.digest, requirements_pex.digest, config_snapshot.digest) ), ) address_references = ", ".join(sorted(config.address.reference() for config in configs)) process = requirements_pex.create_process( python_setup=python_setup, subprocess_encoding_environment=subprocess_encoding_environment, pex_path=f"./bandit.pex", pex_args=generate_args(specified_source_files=specified_source_files, bandit=bandit), input_files=merged_input_files, description=f"Run Bandit on {pluralize(len(configs), 'target')}: {address_references}.", ) result = await Get[FallibleProcessResult](Process, process) return LintResult.from_fallible_process_result(result)
async def flake8_lint( configs: Flake8Configurations, flake8: Flake8, python_setup: PythonSetup, subprocess_encoding_environment: SubprocessEncodingEnvironment, ) -> LintResult: if flake8.options.skip: return LintResult.noop() # NB: Flake8 output depends upon which Python interpreter version it's run with. We ensure that # 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( (config.compatibility for config in configs), python_setup) requirements_pex = await Get[Pex](PexRequest( output_filename="flake8.pex", requirements=PexRequirements(flake8.get_requirement_specs()), interpreter_constraints=interpreter_constraints, entry_point=flake8.get_entry_point(), )) config_path: Optional[str] = flake8.options.config config_snapshot = await Get[Snapshot](PathGlobs( globs=tuple([config_path] if config_path else []), glob_match_error_behavior=GlobMatchErrorBehavior.error, description_of_origin="the option `--flake8-config`", )) all_source_files = await Get[SourceFiles](AllSourceFilesRequest( config.sources for config in configs)) specified_source_files = await Get[SourceFiles]( SpecifiedSourceFilesRequest( (config.sources, config.origin) for config in configs)) merged_input_files = await Get[Digest](DirectoriesToMerge(directories=( all_source_files.snapshot.directory_digest, requirements_pex.directory_digest, config_snapshot.directory_digest, )), ) address_references = ", ".join( sorted(config.address.reference() for config in configs)) process = requirements_pex.create_process( python_setup=python_setup, subprocess_encoding_environment=subprocess_encoding_environment, pex_path=f"./flake8.pex", pex_args=generate_args(specified_source_files=specified_source_files, flake8=flake8), input_files=merged_input_files, description= f"Run Flake8 on {pluralize(len(configs), 'target')}: {address_references}.", ) result = await Get[FallibleProcessResult](Process, process) return LintResult.from_fallible_process_result(result)
def test_respects_passthrough_args(self) -> None: needs_config = FileContent( path="needs_config.py", content= b'"""\nOne line docstring acting like it\'s multiline.\n"""\n', ) target = self.make_target_with_origin([needs_config]) lint_result, fmt_result = self.run_docformatter( [target], passthrough_args="--make-summary-multi-line") assert lint_result == LintResult.noop() assert fmt_result.digest == self.get_digest([needs_config])
def test_skip(self) -> None: target = self.make_target_with_origin([self.bad_source]) result = self.run_pylint([target], skip=True) assert result == LintResult.noop()
def test_skip(self) -> None: target = self.make_target_with_origin([self.bad_source]) lint_result, fmt_result = self.run_isort([target], skip=True) assert lint_result == LintResult.noop() assert fmt_result == FmtResult.noop() assert fmt_result.did_change is False
async def black_lint(configs: BlackConfigurations, black: Black) -> LintResult: if black.options.skip: return LintResult.noop() setup = await Get[Setup](SetupRequest(configs, check_only=True)) result = await Get[FallibleProcessResult](Process, setup.process) return LintResult.from_fallible_process_result(result)
async def pylint_lint( configs: PylintConfigurations, pylint: Pylint, python_setup: PythonSetup, subprocess_encoding_environment: SubprocessEncodingEnvironment, ) -> LintResult: if pylint.options.skip: return LintResult.noop() # 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. addresses = [] for config in configs: addresses.append(config.address) addresses.extend(config.dependencies.value or ()) targets = await Get[Targets](Addresses(addresses)) chrooted_python_sources = await Get[ImportablePythonSources](Targets, targets) # NB: Pylint output depends upon which Python interpreter version it's run with. We ensure that # each target runs with its own interpreter constraints. See # http://pylint.pycqa.org/en/latest/faq.html#what-versions-of-python-is-pylint-supporting. interpreter_constraints = PexInterpreterConstraints.create_from_compatibility_fields( (config.compatibility for config in configs), python_setup) requirements_pex = await Get[Pex](PexRequest( output_filename="pylint.pex", requirements=PexRequirements(pylint.get_requirement_specs()), interpreter_constraints=interpreter_constraints, entry_point=pylint.get_entry_point(), )) config_path: Optional[str] = pylint.options.config config_snapshot = await Get[Snapshot](PathGlobs( globs=tuple([config_path] if config_path else []), glob_match_error_behavior=GlobMatchErrorBehavior.error, description_of_origin="the option `--pylint-config`", )) merged_input_files = await Get[Digest](DirectoriesToMerge(directories=( requirements_pex.directory_digest, config_snapshot.directory_digest, chrooted_python_sources.snapshot.directory_digest, )), ) specified_source_files = await Get[SourceFiles]( SpecifiedSourceFilesRequest( ((config.sources, config.origin) for config in configs), strip_source_roots=True)) address_references = ", ".join( sorted(config.address.reference() for config in configs)) process = requirements_pex.create_process( python_setup=python_setup, subprocess_encoding_environment=subprocess_encoding_environment, pex_path=f"./pylint.pex", pex_args=generate_args(specified_source_files=specified_source_files, pylint=pylint), input_files=merged_input_files, description= f"Run Pylint on {pluralize(len(configs), 'target')}: {address_references}.", ) result = await Get[FallibleProcessResult](Process, process) return LintResult.from_fallible_process_result(result)
def test_passing_source(self) -> None: target = self.make_target_with_origin([self.good_source]) lint_result, fmt_result = self.run_docformatter([target]) assert lint_result == LintResult.noop() assert fmt_result.digest == self.get_digest([self.good_source])
def test_skip(self) -> None: target = self.make_target_with_origin([self.bad_source]) lint_result, fmt_result = self.run_docformatter([target], skip=True) assert lint_result == LintResult.noop() assert fmt_result == FmtResult.noop()
async def pylint_lint( field_sets: PylintFieldSets, pylint: Pylint, python_setup: PythonSetup, subprocess_encoding_environment: SubprocessEncodingEnvironment, ) -> LintResult: if pylint.skip: return LintResult.noop() # 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. addresses_with_dependencies = [] for field_set in field_sets: addresses_with_dependencies.append(field_set.address) addresses_with_dependencies.extend(field_set.dependencies.value or ()) targets = await Get[Targets](Addresses(addresses_with_dependencies)) # NB: Pylint output depends upon which Python interpreter version it's run with. We ensure that # each target runs with its own interpreter constraints. See # http://pylint.pycqa.org/en/latest/faq.html#what-versions-of-python-is-pylint-supporting. interpreter_constraints = PexInterpreterConstraints.create_from_compatibility_fields( (field_set.compatibility for field_set in field_sets), python_setup) # We build one PEX with Pylint requirements and another with all direct 3rd-party dependencies. # Splitting this into two PEXes gives us finer-grained caching. We then merge via `--pex-path`. pylint_pex_request = Get[Pex](PexRequest( output_filename="pylint.pex", requirements=PexRequirements(pylint.get_requirement_specs()), interpreter_constraints=interpreter_constraints, entry_point=pylint.get_entry_point(), )) requirements_pex_request = Get[Pex](PexRequest( output_filename="requirements.pex", requirements=PexRequirements.create_from_requirement_fields( tgt[PythonRequirementsField] for tgt in targets if tgt.has_field(PythonRequirementsField)), interpreter_constraints=interpreter_constraints, )) pylint_runner_pex_request = Get[Pex]( PexRequest( output_filename="pylint_runner.pex", entry_point=pylint.get_entry_point(), interpreter_constraints=interpreter_constraints, additional_args=( "--pex-path", # TODO(John Sirois): Support shading python binaries: # https://github.com/pantsbuild/pants/issues/9206 # Right now any Pylint transitive requirements will shadow corresponding user # requirements which could lead to problems. ":".join(["pylint.pex", "requirements.pex"]), ), )) config_snapshot_request = Get[Snapshot](PathGlobs( globs=[pylint.config] if pylint.config else [], glob_match_error_behavior=GlobMatchErrorBehavior.error, description_of_origin="the option `--pylint-config`", )) prepare_python_sources_request = Get[ImportablePythonSources](Targets, targets) specified_source_files_request = Get[SourceFiles]( SpecifiedSourceFilesRequest( ((field_set.sources, field_set.origin) for field_set in field_sets), strip_source_roots=True, )) ( pylint_pex, requirements_pex, pylint_runner_pex, config_snapshot, prepared_python_sources, specified_source_files, ) = cast( Tuple[Pex, Pex, Pex, Snapshot, ImportablePythonSources, SourceFiles], await MultiGet([ pylint_pex_request, requirements_pex_request, pylint_runner_pex_request, config_snapshot_request, prepare_python_sources_request, specified_source_files_request, ]), ) input_digest = await Get[Digest](MergeDigests(( pylint_pex.digest, requirements_pex.digest, pylint_runner_pex.digest, config_snapshot.digest, prepared_python_sources.snapshot.digest, )), ) address_references = ", ".join( sorted(field_set.address.reference() for field_set in field_sets)) process = requirements_pex.create_process( python_setup=python_setup, subprocess_encoding_environment=subprocess_encoding_environment, pex_path=f"./pylint_runner.pex", 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}.", ) result = await Get[FallibleProcessResult](Process, process) return LintResult.from_fallible_process_result(result, linter_name="Pylint")