def test_filesystem_specs_nonexistent_file(self) -> None: specs = FilesystemSpecs([FilesystemLiteralSpec("demo/fake.txt")]) with pytest.raises(ExecutionError) as exc: self.request_single_product( AddressesWithOrigins, Params(specs, create_options_bootstrapper()), ) assert 'Unmatched glob from file arguments: "demo/fake.txt"' in str(exc.value) ignore_errors_result = self.request_single_product( AddressesWithOrigins, Params(specs, create_options_bootstrapper(args=["--owners-not-found-behavior=ignore"])), ) assert not ignore_errors_result
def test_prepare_scheduler(self): # A core with no services. def create_services(bootstrap_options, legacy_graph_scheduler): return PantsServices() core = PantsDaemonCore(create_services) first_scheduler = core.prepare_scheduler( create_options_bootstrapper(args=["-ldebug"])) second_scheduler = core.prepare_scheduler( create_options_bootstrapper(args=["-lwarn"])) assert first_scheduler is not second_scheduler
def test_transitive_targets(self) -> None: t1 = MockTarget({}, address=Address.parse(":t1")) t2 = MockTarget({Dependencies.alias: [t1.address]}, address=Address.parse(":t2")) d1 = MockTarget({Dependencies.alias: [t1.address]}, address=Address.parse(":d1")) d2 = MockTarget({Dependencies.alias: [t2.address]}, address=Address.parse(":d2")) d3 = MockTarget({}, address=Address.parse(":d3")) root = MockTarget( {Dependencies.alias: [d1.address, d2.address, d3.address]}, address=Address.parse(":root"), ) self.add_to_build_file( "", dedent("""\ target(name='t1') target(name='t2', dependencies=[':t1']) target(name='d1', dependencies=[':t1']) target(name='d2', dependencies=[':t2']) target(name='d3') target(name='root', dependencies=[':d1', ':d2', ':d3']) """), ) direct_deps = self.request_single_product( Targets, Params(DependenciesRequest(root[Dependencies]), create_options_bootstrapper())) assert direct_deps == Targets([d1, d2, d3]) transitive_target = self.request_single_product( TransitiveTarget, Params(WrappedTarget(root), create_options_bootstrapper())) assert transitive_target.root == root assert { dep_transitive_target.root for dep_transitive_target in transitive_target.dependencies } == {d1, d2, d3} transitive_targets = self.request_single_product( TransitiveTargets, Params(Addresses([root.address, d2.address]), create_options_bootstrapper()), ) assert transitive_targets.roots == (root, d2) assert transitive_targets.closure == FrozenOrderedSet( [root, d2, d1, d3, t2, t1])
def make_target( self, source_files: List[FileContent], *, package: Optional[str] = None, name: str = "target", interpreter_constraints: Optional[str] = None, dependencies: Optional[List[Address]] = None, ) -> Target: if not package: package = self.package for source_file in source_files: self.create_file(source_file.path, source_file.content.decode()) source_globs = [ PurePath(source_file.path).name for source_file in source_files ] self.add_to_build_file( package, dedent(f"""\ python_library( name={repr(name)}, sources={source_globs}, dependencies={[str(dep) for dep in dependencies or ()]}, compatibility={repr(interpreter_constraints)}, ) """), ) return self.request_single_product( WrappedTarget, Params( Address(package, target_name=name), create_options_bootstrapper(args=self.global_args), ), ).target
def run_isort( self, targets: List[TargetAdaptorWithOrigin], *, config: Optional[str] = None, passthrough_args: Optional[str] = None, skip: bool = False, ) -> Tuple[LintResult, FmtResult]: args = ["--backend-packages2=pants.backend.python.lint.isort"] if config is not None: self.create_file(relpath=".isort.cfg", contents=config) args.append("--isort-config=.isort.cfg") if passthrough_args: args.append(f"--isort-args='{passthrough_args}'") if skip: args.append(f"--isort-skip") options_bootstrapper = create_options_bootstrapper(args=args) lint_result = self.request_single_product( LintResult, Params(IsortFormatter(tuple(targets)), options_bootstrapper)) input_snapshot = self.request_single_product( Snapshot, DirectoriesToMerge( tuple(target.adaptor.sources.snapshot.directory_digest for target in targets)), ) fmt_result = self.request_single_product( FmtResult, Params( IsortFormatter(tuple(targets), prior_formatter_result=input_snapshot), options_bootstrapper, ), ) return lint_result, fmt_result
def assert_injected( self, *, source_roots: List[str], original_declared_files: List[str], original_undeclared_files: List[str], expected_discovered: List[str], ) -> None: for f in original_undeclared_files: self.create_file(f, "# undeclared") request = AncestorFilesRequest( "__init__.py", self.make_snapshot( {fp: "# declared" for fp in original_declared_files}), ) bootstrapper = create_options_bootstrapper( args=[f"--source-root-patterns={source_roots}"]) result = self.request_single_product(AncestorFiles, Params(request, bootstrapper)).snapshot assert list(result.files) == sorted(expected_discovered) materialized_result = self.request_single_product( DigestContents, result.digest) for file_content in materialized_result: path = file_content.path if not path.endswith("__init__.py"): continue assert path in original_declared_files or path in expected_discovered expected = b"# declared" if path in original_declared_files else b"# undeclared" assert file_content.content == expected
def assert_sources( self, expected_files, expected_packages, expected_namespace_packages, expected_package_data, addrs, ): srcs = self.request_single_product( SetupPySources, Params( SetupPySourcesRequest(Targets( [self.tgt(addr) for addr in addrs]), py2=False), create_options_bootstrapper( args=["--source-root-patterns=src/python"]), ), ) chroot_snapshot = self.request_single_product(Snapshot, Params(srcs.digest)) assert sorted(expected_files) == sorted(chroot_snapshot.files) assert sorted(expected_packages) == sorted(srcs.packages) assert sorted(expected_namespace_packages) == sorted( srcs.namespace_packages) assert expected_package_data == dict(srcs.package_data)
def run_pylint( self, targets: List[TargetWithOrigin], *, config: Optional[str] = None, passthrough_args: Optional[str] = None, skip: bool = False, additional_args: Optional[List[str]] = None, ) -> LintResult: args = ["--backend-packages2=pants.backend.python.lint.pylint"] if config: self.create_file(relpath="pylintrc", contents=config) args.append("--pylint-config=pylintrc") if passthrough_args: args.append(f"--pylint-args='{passthrough_args}'") if skip: args.append(f"--pylint-skip") if additional_args: args.extend(additional_args) return self.request_single_product( LintResult, Params( PylintFieldSets(PylintFieldSet.create(tgt) for tgt in targets), create_options_bootstrapper(args=args), ), )
def test_filter_field_sets(self) -> None: @dataclass(frozen=True) class MockFieldSet(FieldSet): sources: Sources # Another field to demo that we will preserve the whole FieldSet data structure. tags: Tags self.create_file("f1.txt") valid_addr = Address("", target_name="valid") valid_field_set = MockFieldSet( valid_addr, Sources(["f1.txt"], address=valid_addr), Tags(None, address=valid_addr) ) empty_addr = Address("", target_name="empty") empty_field_set = MockFieldSet( empty_addr, Sources(None, address=empty_addr), Tags(None, address=empty_addr) ) result = self.request_single_product( FieldSetsWithSources, Params( FieldSetsWithSourcesRequest([valid_field_set, empty_field_set]), create_options_bootstrapper(), ), ) assert tuple(result) == (valid_field_set,)
def run_docformatter( self, targets: List[TargetWithOrigin], *, passthrough_args: Optional[str] = None, skip: bool = False, ) -> Tuple[LintResult, FmtResult]: args = ["--backend-packages2=pants.backend.python.lint.docformatter"] if passthrough_args: args.append(f"--docformatter-args='{passthrough_args}'") if skip: args.append(f"--docformatter-skip") options_bootstrapper = create_options_bootstrapper(args=args) configs = [DocformatterConfiguration.create(tgt) for tgt in targets] lint_result = self.request_single_product( LintResult, Params(DocformatterConfigurations(configs), options_bootstrapper)) input_snapshot = self.request_single_product( SourceFiles, Params(AllSourceFilesRequest(config.sources for config in configs), options_bootstrapper), ) fmt_result = self.request_single_product( FmtResult, Params( DocformatterConfigurations( configs, prior_formatter_result=input_snapshot), options_bootstrapper, ), ) return lint_result, fmt_result
def run_pytest(self, *, passthrough_args: Optional[str] = None) -> TestResult: args = [ "--backend-packages2=pants.backend.python", "--pytest-version=pytest>=4.6.6,<4.7", # so that we can run Python 2 tests ] if passthrough_args: args.append(f"--pytest-args='{passthrough_args}'") options_bootstrapper = create_options_bootstrapper(args=args) target = PythonTestsAdaptor(address=BuildFileAddress( rel_path=f"{self.source_root}/BUILD", target_name="target"), ) test_result = self.request_single_product( TestResult, Params(target, options_bootstrapper)) debug_request = self.request_single_product( TestDebugRequest, Params(target, options_bootstrapper), ) debug_result = InteractiveRunner( self.scheduler).run_local_interactive_process(debug_request.ipr) if test_result.status == Status.SUCCESS: assert debug_result.process_exit_code == 0 else: assert debug_result.process_exit_code != 0 return test_result
def test_normal_resolution(self) -> None: self.add_to_build_file("src/smalltalk", "smalltalk()") addr = Address.parse("src/smalltalk") deps = Addresses([Address.parse("//:dep1"), Address.parse("//:dep2")]) deps_field = Dependencies(deps, address=addr) assert (self.request_single_product( Addresses, Params(DependenciesRequest(deps_field), create_options_bootstrapper())) == deps) # Also test that we handle no dependencies. empty_deps_field = Dependencies(None, address=addr) assert self.request_single_product( Addresses, Params(DependenciesRequest(empty_deps_field), create_options_bootstrapper())) == Addresses([])
def test_dependency_inference(self) -> None: self.add_to_build_file( "", dedent("""\ smalltalk(name='inferred1') smalltalk(name='inferred2') smalltalk(name='inferred3') smalltalk(name='provided') """), ) self.create_file("demo/f1.st", "//:inferred1\n//:inferred2\n") self.create_file("demo/f2.st", "//:inferred3\n") self.add_to_build_file( "demo", "smalltalk(sources=['*.st'], dependencies=['//:provided'])") deps_field = Dependencies([Address.parse("//:provided")], address=Address.parse("demo")) result = self.request_single_product( Addresses, Params( DependenciesRequest(deps_field), create_options_bootstrapper(args=["--dependency-inference"]), ), ) assert result == Addresses( sorted( Address.parse(addr) for addr in [ "//:inferred1", "//:inferred2", "//:inferred3", "//:provided" ]))
def run_mypy( self, targets: List[Target], *, config: Optional[str] = None, passthrough_args: Optional[str] = None, skip: bool = False, additional_args: Optional[List[str]] = None, ) -> Sequence[TypecheckResult]: args = list(self.global_args) if config: self.create_file(relpath="mypy.ini", contents=config) args.append("--mypy-config=mypy.ini") if passthrough_args: args.append(f"--mypy-args='{passthrough_args}'") if skip: args.append("--mypy-skip") if additional_args: args.extend(additional_args) result = self.request_single_product( TypecheckResults, Params( MyPyRequest(MyPyFieldSet.create(tgt) for tgt in targets), create_options_bootstrapper(args=args), ), ) return result.results
def run_mypy( self, targets: List[TargetWithOrigin], *, config: Optional[str] = None, passthrough_args: Optional[str] = None, skip: bool = False, additional_args: Optional[List[str]] = None, ) -> TypecheckResults: args = [ "--backend-packages=pants.backend.python", "--backend-packages=pants.backend.python.typecheck.mypy", "--source-root-patterns=['src/python', 'tests/python']", ] if config: self.create_file(relpath="mypy.ini", contents=config) args.append("--mypy-config=mypy.ini") if passthrough_args: args.append(f"--mypy-args='{passthrough_args}'") if skip: args.append("--mypy-skip") if additional_args: args.extend(additional_args) return self.request_single_product( TypecheckResults, Params( MyPyRequest(MyPyFieldSet.create(tgt) for tgt in targets), create_options_bootstrapper(args=args), ), )
def test_map_first_party_modules_to_addresses(self) -> None: options_bootstrapper = create_options_bootstrapper(args=[ "--source-root-patterns=['src/python', 'tests/python', 'build-support']" ]) util_addr = Address.parse("src/python/project/util") self.create_files("src/python/project/util", ["dirutil.py", "tarutil.py"]) self.add_to_build_file("src/python/project/util", "python_library()") # A module with two owners should not be resolved. self.create_file("src/python/two_owners.py") self.add_to_build_file("src/python", "python_library()") self.create_file("build-support/two_owners.py") self.add_to_build_file("build-support", "python_library()") # A package module self.create_file("tests/python/project_test/demo_test/__init__.py") self.add_to_build_file("tests/python/project_test/demo_test", "python_library()") result = self.request_single_product(FirstPartyModuleToAddressMapping, options_bootstrapper) assert result.mapping == FrozenDict({ "project.util.dirutil": util_addr, "project.util.tarutil": util_addr, "project_test.demo_test": Address.parse("tests/python/project_test/demo_test"), })
def run_pylint( self, targets: List[Target], *, config: Optional[str] = None, passthrough_args: Optional[str] = None, skip: bool = False, additional_args: Optional[List[str]] = None, ) -> Sequence[LintResult]: args = list(self.global_args) if config: self.create_file(relpath="pylintrc", contents=config) args.append("--pylint-config=pylintrc") if passthrough_args: args.append(f"--pylint-args='{passthrough_args}'") if skip: args.append("--pylint-skip") if additional_args: args.extend(additional_args) results = self.request_single_product( LintResults, Params( PylintRequest(PylintFieldSet.create(tgt) for tgt in targets), create_options_bootstrapper(args=args), ), ) return results.results
def run_black( self, source_files: List[FileContent], *, config: Optional[str] = None, passthrough_args: Optional[str] = None, skip: bool = False, ) -> Tuple[LintResult, FmtResult]: args = ["--backend-packages2=pants.backend.python.lint.black"] if config is not None: self.create_file(relpath="pyproject.toml", contents=config) args.append("--black-config=pyproject.toml") if passthrough_args: args.append(f"--black-args='{passthrough_args}'") if skip: args.append(f"--black-skip") input_snapshot = self.request_single_product(Snapshot, InputFilesContent(source_files)) target_adaptor = TargetAdaptor( sources=EagerFilesetWithSpec('test', {'globs': []}, snapshot=input_snapshot), address=Address.parse("test:target"), ) lint_target = BlackTarget(target_adaptor) fmt_target = BlackTarget(target_adaptor, prior_formatter_result_digest=input_snapshot.directory_digest) options_bootstrapper = create_options_bootstrapper(args=args) lint_result = self.request_single_product(LintResult, Params(lint_target, options_bootstrapper)) fmt_result = self.request_single_product(FmtResult, Params(fmt_target, options_bootstrapper)) return lint_result, fmt_result
def run_bandit( self, targets: List[Target], *, config: Optional[str] = None, passthrough_args: Optional[str] = None, skip: bool = False, additional_args: Optional[List[str]] = None, ) -> LintResults: args = ["--backend-packages=pants.backend.python.lint.bandit"] if config: self.create_file(relpath=".bandit", contents=config) args.append("--bandit-config=.bandit") if passthrough_args: args.append(f"--bandit-args={passthrough_args}") if skip: args.append("--bandit-skip") if additional_args: args.extend(additional_args) return self.request_single_product( LintResults, Params( BanditRequest(BanditFieldSet.create(tgt) for tgt in targets), create_options_bootstrapper(args=args), ), )
def run_isort( self, targets: List[TargetWithOrigin], *, config: Optional[str] = None, passthrough_args: Optional[str] = None, skip: bool = False, ) -> Tuple[LintResult, FmtResult]: args = ["--backend-packages2=pants.backend.python.lint.isort"] if config is not None: self.create_file(relpath=".isort.cfg", contents=config) args.append("--isort-config=.isort.cfg") if passthrough_args: args.append(f"--isort-args='{passthrough_args}'") if skip: args.append(f"--isort-skip") options_bootstrapper = create_options_bootstrapper(args=args) configs = [IsortConfiguration.create(tgt) for tgt in targets] lint_result = self.request_single_product( LintResult, Params(IsortConfigurations(configs), options_bootstrapper)) input_snapshot = self.request_single_product( SourceFiles, Params(AllSourceFilesRequest(config.sources for config in configs), options_bootstrapper), ) fmt_result = self.request_single_product( FmtResult, Params( IsortConfigurations(configs, prior_formatter_result=input_snapshot), options_bootstrapper, ), ) return lint_result, fmt_result
def create_pex_and_get_all_data( self, *, requirements=PexRequirements(), entry_point=None, interpreter_constraints=PexInterpreterConstraints(), input_files: Optional[Digest] = None, additional_pants_args: Tuple[str, ...] = (), additional_pex_args: Tuple[str, ...] = (), ) -> Dict: request = CreatePex( output_filename="test.pex", requirements=requirements, interpreter_constraints=interpreter_constraints, entry_point=entry_point, input_files_digest=input_files, additional_args=additional_pex_args, ) requirements_pex = self.request_single_product( Pex, Params( request, create_options_bootstrapper( args=["--backend-packages2=pants.backend.python", *additional_pants_args] ), ), ) self.scheduler.materialize_directory( DirectoryToMaterialize(requirements_pex.directory_digest), ) with zipfile.ZipFile(os.path.join(self.build_root, "test.pex"), "r") as pex: with pex.open("PEX-INFO", "r") as pex_info: pex_info_content = pex_info.readline().decode() pex_list = pex.namelist() return {"pex": requirements_pex, "info": json.loads(pex_info_content), "files": pex_list}
def test_target_adaptor_parsed_correctly(self) -> None: self.add_to_build_file( "helloworld", dedent("""\ mock_tgt( fake_field=42, dependencies=[ # Because we don't follow dependencies or even parse dependencies, this # self-cycle should be fine. "helloworld", ":sibling", "helloworld/util", "helloworld/util:tests", ], ) """), ) addr = Address("helloworld") target_adaptor = self.request_single_product( TargetAdaptor, Params(addr, create_options_bootstrapper())) assert target_adaptor.name == "helloworld" assert target_adaptor.type_alias == "mock_tgt" assert target_adaptor.kwargs["dependencies"] == [ "helloworld", ":sibling", "helloworld/util", "helloworld/util:tests", ] # NB: TargetAdaptors do not validate what fields are valid. The Target API should error # when encountering this, but it's fine at this stage. assert target_adaptor.kwargs["fake_field"] == 42
def run_pytest( self, *, passthrough_args: Optional[str] = None, origin: Optional[OriginSpec] = None, ) -> TestResult: args = [ "--backend-packages2=pants.backend.python", # pin to lower versions so that we can run Python 2 tests "--pytest-version=pytest>=4.6.6,<4.7", "--pytest-pytest-plugins=['zipp==1.0.0']", ] if passthrough_args: args.append(f"--pytest-args='{passthrough_args}'") options_bootstrapper = create_options_bootstrapper(args=args) address = Address(self.source_root, "target") if origin is None: origin = SingleAddress(directory=address.spec_path, name=address.target_name) tgt = PythonTests({}, address=address) params = Params( PythonTestConfiguration.create(TargetWithOrigin(tgt, origin)), options_bootstrapper) test_result = self.request_single_product(TestResult, params) debug_request = self.request_single_product(TestDebugRequest, params) debug_result = InteractiveRunner( self.scheduler).run_local_interactive_process(debug_request.ipr) if test_result.status == Status.SUCCESS: assert debug_result.process_exit_code == 0 else: assert debug_result.process_exit_code != 0 return test_result
def test_adds_missing_inits_and_strips_source_roots(self) -> None: target_with_init = self.make_hydrated_target(source_paths=[ "src/python/project/lib.py", "src/python/project/__init__.py" ], ) target_without_init = self.make_hydrated_target(source_paths=[ "tests/python/test_project/f1.py", "tests/python/test_project/f2.py" ], ) files_target = self.make_hydrated_target( source_paths=["src/python/project/resources/loose_file.txt"], type_alias=Files.alias(), ) result = self.request_single_product( ChrootedPythonSources, Params( HydratedTargets( [target_with_init, target_without_init, files_target]), create_options_bootstrapper(), ), ) assert sorted(result.snapshot.files) == sorted([ "project/lib.py", "project/__init__.py", "test_project/f1.py", "test_project/f2.py", "test_project/__init__.py", "src/python/project/resources/loose_file.txt", ])
def test_adds_missing_inits_and_strips_source_roots(self) -> None: target_with_init = self.create_target( parent_directory="src/python/project", files=["lib.py", "__init__.py"]) target_without_init = self.create_target( parent_directory="src/python/test_project", files=["f1.py", "f2.py"]) files_target = self.create_target( parent_directory="src/python/project/resources", files=["loose_file.txt"], target_cls=Files, ) result = self.request_single_product( ImportablePythonSources, Params( Targets([target_with_init, target_without_init, files_target]), create_options_bootstrapper(), ), ) assert sorted(result.snapshot.files) == sorted([ "project/lib.py", "project/__init__.py", "test_project/f1.py", "test_project/f2.py", "test_project/__init__.py", "src/python/project/resources/loose_file.txt", ])
def test_transitive_targets_tolerates_subtarget_cycles(self) -> None: """For generated subtargets, we should tolerate cycles between targets. This only works with generated subtargets, so we use explicit file dependencies in this test. """ self.create_files("", ["dep.txt", "t1.txt", "t2.txt"]) self.add_to_build_file( "", dedent( """\ target(name='dep', sources=['dep.txt']) target(name='t1', sources=['t1.txt'], dependencies=['dep.txt:dep', 't2.txt:t2']) target(name='t2', sources=['t2.txt'], dependencies=['t1.txt:t1']) """ ), ) result = self.request_single_product( TransitiveTargets, Params(Addresses([Address("", target_name="t2")]), create_options_bootstrapper()), ) assert len(result.roots) == 1 assert result.roots[0].address == Address("", relative_file_path="t2.txt", target_name="t2") assert [tgt.address for tgt in result.dependencies] == [ Address("", relative_file_path="t1.txt", target_name="t1"), Address("", relative_file_path="dep.txt", target_name="dep"), Address("", relative_file_path="t2.txt", target_name="t2"), ]
def test_map_third_party_modules_to_addresses(self) -> None: self.add_to_build_file( "3rdparty/python", dedent("""\ python_requirement_library( name='ansicolors', requirements=['ansicolors==1.21'], module_mapping={'ansicolors': ['colors']}, ) python_requirement_library( name='req1', requirements=['req1', 'two_owners'], ) python_requirement_library( name='un_normalized', requirements=['Un-Normalized-Project>3', 'two_owners'], ) """), ) result = self.request_single_product( ThirdPartyModuleToAddressMapping, Params(create_options_bootstrapper())) assert result.mapping == FrozenDict({ "colors": Address.parse("3rdparty/python:ansicolors"), "req1": Address.parse("3rdparty/python:req1"), "un_normalized_project": Address.parse("3rdparty/python:un_normalized"), })
def run_bandit( self, source_files: List[FileContent], *, config: Optional[str] = None, passthrough_args: Optional[str] = None, interpreter_constraints: Optional[str] = None, skip: bool = False, ) -> LintResult: args = ["--backend-packages2=pants.backend.python.lint.bandit"] if config: # TODO: figure out how to get this file to exist... self.create_file(relpath=".bandit", contents=config) args.append("--bandit-config=.bandit") if passthrough_args: args.append(f"--bandit-args={passthrough_args}") if skip: args.append(f"--bandit-skip") input_snapshot = self.request_single_product(Snapshot, InputFilesContent(source_files)) target = BanditTarget( PythonTargetAdaptor( sources=EagerFilesetWithSpec('test', {'globs': []}, snapshot=input_snapshot), address=Address.parse("test:target"), compatibility=[interpreter_constraints] if interpreter_constraints else None, ) ) return self.request_single_product( LintResult, Params(target, create_options_bootstrapper(args=args)), )
def assert_requirements(self, expected_req_strs, addr): reqs = self.request_single_product( ExportedTargetRequirements, Params(DependencyOwner(ExportedTarget(self.tgt(addr))), create_options_bootstrapper()), ) assert sorted(expected_req_strs) == list(reqs)
def run_docformatter( self, targets: List[Target], *, passthrough_args: Optional[str] = None, skip: bool = False, ) -> Tuple[LintResults, FmtResult]: args = ["--backend-packages=pants.backend.python.lint.docformatter"] if passthrough_args: args.append(f"--docformatter-args='{passthrough_args}'") if skip: args.append("--docformatter-skip") options_bootstrapper = create_options_bootstrapper(args=args) field_sets = [DocformatterFieldSet.create(tgt) for tgt in targets] lint_results = self.request_single_product( LintResults, Params(DocformatterRequest(field_sets), options_bootstrapper)) input_sources = self.request_single_product( SourceFiles, Params( SourceFilesRequest(field_set.sources for field_set in field_sets), options_bootstrapper, ), ) fmt_result = self.request_single_product( FmtResult, Params( DocformatterRequest( field_sets, prior_formatter_result=input_sources.snapshot), options_bootstrapper, ), ) return lint_results, fmt_result