def test_single_non_test_target(self): bfaddr = BuildFileAddress(None, 'bin', 'some/dir') target_adaptor = PythonBinaryAdaptor(type_alias='python_binary') with self.captured_logging(logging.INFO): # Note that this is not the same error message the end user will see, as we're resolving # union Get requests in run_rule, not the real engine. But this test still asserts that # we error when we expect to error. with self.assertRaisesRegex( AssertionError, r'Rule requested: .* which cannot be satisfied.'): run_rule( coordinator_of_tests, rule_args=[ HydratedTarget(bfaddr.to_address(), target_adaptor, ()), UnionMembership( union_rules={TestTarget: [PythonTestsAdaptor]}), AddressProvenanceMap( bfaddr_to_spec={ bfaddr: SingleAddress(directory='some/dir', name='bin'), }), ], mock_gets=[ MockGet( product_type=TestResult, subject_type=PythonTestsAdaptor, mock=lambda _: TestResult(status=Status.SUCCESS, stdout='foo', stderr=''), ), ], )
def test_globbed_non_test_target(self): bfaddr = BuildFileAddress(None, 'bin', 'some/dir') target_adaptor = PythonBinaryAdaptor(type_alias='python_binary') with self.captured_logging(logging.INFO): result = run_rule( coordinator_of_tests, rule_args=[ HydratedTarget(bfaddr.to_address(), target_adaptor, ()), UnionMembership( union_rules={TestTarget: [PythonTestsAdaptor]}), AddressProvenanceMap( bfaddr_to_spec={ bfaddr: DescendantAddresses(directory='some/dir') }), ], mock_gets=[ MockGet( product_type=TestResult, subject_type=PythonTestsAdaptor, mock=lambda _: TestResult( status=Status.SUCCESS, stdout='foo', stderr=''), ), ], ) self.assertEqual(result, AddressAndTestResult(bfaddr.to_address(), None))
def test_coordinator_globbed_non_test_target(self): bfaddr = BuildFileAddress(rel_path='some/dir', target_name='bin') result = self.run_coordinator_of_tests( address=bfaddr.to_address(), bfaddr_to_spec={bfaddr: DescendantAddresses(directory='some/dir')}, test_target_type=False, ) assert result == AddressAndTestResult(bfaddr.to_address(), None)
def test_coordinator_globbed_test_target(self): bfaddr = BuildFileAddress(rel_path='some/dir', target_name='tests') result = self.run_coordinator_of_tests( address=bfaddr.to_address(), bfaddr_to_spec={bfaddr: DescendantAddresses(directory='some/dir')}) assert result == AddressAndTestResult( bfaddr.to_address(), TestResult(status=Status.SUCCESS, stdout='foo', stderr=''))
def test_scan_addresses_with_root(self): self.add_to_build_file('BUILD', 'target(name="foo")') subdir_build_file = self.add_to_build_file('subdir/BUILD', 'target(name="bar")') subdir_suffix_build_file = self.add_to_build_file('subdir/BUILD.suffix', 'target(name="baz")') subdir = os.path.join(self.build_root, 'subdir') self.assertEquals({BuildFileAddress(subdir_build_file, 'bar'), BuildFileAddress(subdir_suffix_build_file, 'baz')}, self.address_mapper.scan_addresses(root=subdir))
def test_scan_addresses(self): root_build_file = self.add_to_build_file('BUILD', 'target(name="foo")') subdir_build_file = self.add_to_build_file('subdir/BUILD', 'target(name="bar")') subdir_suffix_build_file = self.add_to_build_file('subdir/BUILD.suffix', 'target(name="baz")') with open(os.path.join(self.build_root, 'BUILD.invalid.suffix'), 'w') as invalid_build_file: invalid_build_file.write('target(name="foobar")') self.assertEquals({BuildFileAddress(root_build_file, 'foo'), BuildFileAddress(subdir_build_file, 'bar'), BuildFileAddress(subdir_suffix_build_file, 'baz')}, self.address_mapper.scan_addresses())
def test_coordinator_single_non_test_target(self): bfaddr = BuildFileAddress(None, 'bin', 'some/dir') # Note that this is not the same error message the end user will see, as we're resolving # union Get requests in run_rule, not the real engine. But this test still asserts that # we error when we expect to error. with self.assertRaisesRegex(AssertionError, r'Rule requested: .* which cannot be satisfied.'): self.run_coordinator_of_tests( address=bfaddr.to_address(), bfaddr_to_spec={bfaddr: SingleAddress(directory='some/dir', name='bin')}, test_target_type=False, )
def test_build_file_forms(self): with self.workspace('a/b/c/BUILD') as root_dir: build_file = BuildFile(FileSystemProjectTree(root_dir), relpath='a/b/c/BUILD') self.assert_address('a/b/c', 'c', BuildFileAddress(build_file)) self.assert_address('a/b/c', 'foo', BuildFileAddress(build_file, target_name='foo')) self.assertEqual('a/b/c:foo', BuildFileAddress(build_file, target_name='foo').spec) with self.workspace('BUILD') as root_dir: build_file = BuildFile(FileSystemProjectTree(root_dir), relpath='BUILD') self.assert_address('', 'foo', BuildFileAddress(build_file, target_name='foo')) self.assertEqual('//:foo', BuildFileAddress(build_file, target_name='foo').spec)
def test_is_declaring_file(self): scheduler = mock.Mock() mapper = LegacyAddressMapper(scheduler, '') self.assertTrue(mapper.is_declaring_file(Address('path', 'name'), 'path/BUILD')) self.assertTrue(mapper.is_declaring_file(Address('path', 'name'), 'path/BUILD.suffix')) self.assertFalse(mapper.is_declaring_file(Address('path', 'name'), 'path/not_a_build_file')) self.assertFalse(mapper.is_declaring_file(Address('path', 'name'), 'differing-path/BUILD')) self.assertFalse(mapper.is_declaring_file( BuildFileAddress(target_name='name', rel_path='path/BUILD.new'), 'path/BUILD')) self.assertTrue(mapper.is_declaring_file( BuildFileAddress(target_name='name', rel_path='path/BUILD'), 'path/BUILD'))
def test_scan_addresses_with_excludes(self): root_build_file = self.add_to_build_file('BUILD', 'target(name="foo")') self.add_to_build_file('subdir/BUILD', 'target(name="bar")') spec_excludes = [os.path.join(self.build_root, 'subdir')] self.assertEquals( {BuildFileAddress(root_build_file, 'foo')}, self.address_mapper.scan_addresses(spec_excludes=spec_excludes))
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 build_file_addresses(self) -> Tuple[BuildFileAddress, ...]: return tuple( BuildFileAddress( rel_path=path, address=Address(spec_path=self.namespace, target_name=name) ) for name, (path, _) in self.name_to_target_adaptors.items() )
def __init__(self, name, build_file, build_file_source_lines, target_source_lines, target_interval, dependencies, dependencies_interval): """See BuildFileManipulator.load() for how to construct one as a user.""" self.name = name self.build_file = build_file self.target_address = BuildFileAddress(build_file, name) self._build_file_source_lines = build_file_source_lines self._target_source_lines = target_source_lines self._target_interval = target_interval self._dependencies_interval = dependencies_interval self._dependencies_by_address = {} for dep in dependencies: dep_address = Address.parse(dep.spec, relative_to=build_file.spec_path) if dep_address in self._dependencies_by_address: raise BuildTargetParseError('The address {dep_address} occurred multiple times in the ' 'dependency specs for target {name} in {build_file}. ' .format(dep_address=dep_address.spec, name=name, build_file=build_file)) self._dependencies_by_address[dep_address] = dep
def run(console: Console, workspace: Workspace, runner: InteractiveRunner, bfa: BuildFileAddress) -> Run: target = bfa.to_address() binary = yield Get(CreatedBinary, Address, target) with temporary_dir(cleanup=True) as tmpdir: dirs_to_materialize = (DirectoryToMaterialize( path=str(tmpdir), directory_digest=binary.digest), ) workspace.materialize_directories(dirs_to_materialize) console.write_stdout(f"Running target: {target}\n") full_path = str(Path(tmpdir, binary.binary_name)) run_request = InteractiveProcessRequest( argv=[full_path], run_in_workspace=True, ) try: result = runner.run_local_interactive_process(run_request) exit_code = result.process_exit_code if result.process_exit_code == 0: console.write_stdout(f"{target} ran successfully.\n") else: console.write_stderr( f"{target} failed with code {result.process_exit_code}!\n") except Exception as e: console.write_stderr( f"Exception when attempting to run {target} : {e}\n") exit_code = -1 yield Run(exit_code)
def test_output_mixed(self): console = MockConsole(use_colors=False) options = MockOptions(debug=False) runner = InteractiveRunner(self.scheduler) target1 = self.make_build_target_address( "testprojects/tests/python/pants/passes") target2 = self.make_build_target_address( "testprojects/tests/python/pants/fails") def make_result(target): if target == target1: tr = TestResult(status=Status.SUCCESS, stdout='I passed\n', stderr='') elif target == target2: tr = TestResult(status=Status.FAILURE, stdout='I failed\n', stderr='') else: raise Exception("Unrecognised target") return AddressAndTestResult(target, tr) def make_debug_request(target): request = TestDebugRequest(ipr=self.make_successful_ipr( ) if target == target1 else self.make_failure_ipr()) return AddressAndDebugRequest(target, request) res = run_rule( run_tests, rule_args=[console, options, runner, (target1, target2)], mock_gets=[ MockGet(product_type=AddressAndTestResult, subject_type=Address, mock=make_result), MockGet(product_type=AddressAndDebugRequest, subject_type=Address, mock=make_debug_request), MockGet(product_type=BuildFileAddress, subject_type=BuildFileAddresses, mock=lambda tgt: BuildFileAddress( rel_path=f'{tgt.spec_path}/BUILD', target_name=tgt.target_name, )), ], ) self.assertEqual(1, res.exit_code) self.assertEquals( console.stdout.getvalue(), dedent("""\ testprojects/tests/python/pants/passes stdout: I passed testprojects/tests/python/pants/fails stdout: I failed testprojects/tests/python/pants/passes ..... SUCCESS testprojects/tests/python/pants/fails ..... FAILURE """))
def make_build_target_address(spec): address = Address.parse(spec) return BuildFileAddress( build_file=None, target_name=address.target_name, rel_path=f'{address.spec_path}/BUILD', )
def make_build_target_address(self, spec): address = Address.parse(spec) return BuildFileAddress( build_file=None, target_name=address.target_name, rel_path='{}/BUILD'.format(address.spec_path), )
def single_target_run( self, *, console: MockConsole, program_text: bytes, address_spec: str, ) -> Run: workspace = Workspace(self.scheduler) interactive_runner = InteractiveRunner(self.scheduler) address = Address.parse(address_spec) bfa = BuildFileAddress(build_file=None, target_name=address.target_name, rel_path=f'{address.spec_path}/BUILD') BuildRoot().path = self.build_root res = run_rule( run, rule_args=[ console, workspace, interactive_runner, BuildRoot(), bfa, MockOptions(args=[]) ], mock_gets=[ MockGet(product_type=CreatedBinary, subject_type=Address, mock=lambda _: self.create_mock_binary(program_text)), ], ) return cast(Run, res)
def __call__(self, *args, **kwargs): addressable = self._addressable_factory.capture(*args, **kwargs) addressable_name = addressable.addressed_name if addressable_name: address = BuildFileAddress(build_file=self._build_file, target_name=addressable_name) self._registration_callback(address, addressable) return addressable
def test_is_declaring_file(self) -> None: scheduler = unittest.mock.Mock() mapper = LegacyAddressMapper(scheduler, "") self.assertTrue(mapper.is_declaring_file(Address("path", "name"), "path/BUILD")) self.assertTrue(mapper.is_declaring_file(Address("path", "name"), "path/BUILD.suffix")) self.assertFalse(mapper.is_declaring_file(Address("path", "name"), "path/not_a_build_file")) self.assertFalse(mapper.is_declaring_file(Address("path", "name"), "differing-path/BUILD")) self.assertFalse( mapper.is_declaring_file( BuildFileAddress(target_name="name", rel_path="path/BUILD.new"), "path/BUILD" ) ) self.assertTrue( mapper.is_declaring_file( BuildFileAddress(target_name="name", rel_path="path/BUILD"), "path/BUILD" ) )
def addressables(self) -> Dict[BuildFileAddress, ThinAddressableObject]: """Return a mapping from BuildFileAddress to thin addressable objects in this namespace. :rtype: dict from `BuildFileAddress` to thin addressable objects. """ return { BuildFileAddress(rel_path=path, target_name=name): obj for name, (path, obj) in self.objects_by_name.items() }
def parse_variants(address): target_name, at_sign, variants_str = address.target_name.partition('@') if not at_sign: return address, None variants = _extract_variants(address, variants_str) if variants_str else None if isinstance(address, BuildFileAddress): normalized_address = BuildFileAddress(rel_path=address.rel_path, target_name=target_name) else: normalized_address = Address(spec_path=address.spec_path, target_name=target_name) return normalized_address, variants
def addressables(self): """Return a mapping from BuildFileAddress to thin addressable objects in this namespace. :rtype: dict from :class:`pants.build_graph.address.BuildFileAddress` to thin addressable objects. """ return { BuildFileAddress(rel_path=path, target_name=name): obj for name, (path, obj) in self.objects_by_name.items() }
def test_trivial_target(self): self.add_to_build_file('BUILD', 'fake(name="foozle")') build_file = self.create_buildfile('BUILD') address_map = self.build_file_parser.parse_build_file(build_file) self.assertEqual(len(address_map), 1) address, proxy = address_map.popitem() self.assertEqual(address, BuildFileAddress(build_file=build_file, target_name='foozle')) self.assertEqual(proxy.addressed_name, 'foozle') self.assertEqual(proxy.addressed_type, ErrorTarget)
def test_empty_target_succeeds(self) -> None: # NB: Because this particular edge case should early return, we can avoid providing valid # mocked yield gets for most of the rule's body. Future tests added to this file will need to # provide valid mocks instead. unimplemented_mock = lambda _: NotImplemented target = PythonTestsAdaptor( address=BuildFileAddress(target_name="target", rel_path="test")) result: TestResult = run_rule( run_python_test, rule_args=[ target, PyTest.global_instance(), PythonSetup.global_instance(), SubprocessEnvironment.global_instance(), ], mock_gets=[ MockGet(product_type=TransitiveHydratedTargets, subject_type=BuildFileAddresses, mock=lambda _: TransitiveHydratedTargets(roots=(), closure=())), MockGet( product_type=SourceRootStrippedSources, subject_type=Address, mock=lambda _: SourceRootStrippedSources(snapshot= EMPTY_SNAPSHOT), ), MockGet( product_type=SourceRootStrippedSources, subject_type=HydratedTarget, mock=unimplemented_mock, ), MockGet( product_type=Digest, subject_type=DirectoriesToMerge, mock=unimplemented_mock, ), MockGet( product_type=InjectedInitDigest, subject_type=Digest, mock=unimplemented_mock, ), MockGet( product_type=Pex, subject_type=CreatePex, mock=unimplemented_mock, ), MockGet( product_type=FallibleExecuteProcessResult, subject_type=ExecuteProcessRequest, mock=unimplemented_mock, ), ], ) self.assertEqual(result.status, Status.SUCCESS)
def test_globbed_test_target(self): bfaddr = BuildFileAddress(None, 'tests', 'some/dir') target_adaptor = PythonTestsAdaptor(type_alias='python_tests') with self.captured_logging(logging.INFO): result = run_rule( coordinator_of_tests, HydratedTarget(bfaddr.to_address(), target_adaptor, ()), UnionMembership(union_rules={TestTarget: [PythonTestsAdaptor]}), AddressProvenanceMap(bfaddr_to_spec={ bfaddr: DescendantAddresses(directory='some/dir') }), { (TestResult, PythonTestsAdaptor): lambda _: TestResult(status=Status.SUCCESS, stdout='foo', stderr=''), }) self.assertEqual( result, AddressAndTestResult(bfaddr.to_address(), TestResult(status=Status.SUCCESS, stdout='foo', stderr='')) )
def sources_for( self, package_relative_path_globs: List[str], package_dir: str = "", ) -> EagerFilesetWithSpec: sources_field = SourcesField( address=BuildFileAddress( rel_path=os.path.join(package_dir, "BUILD"), target_name="_bogus_target_for_test", ), arg="sources", source_globs=SourceGlobs(*package_relative_path_globs), ) field = self.scheduler.product_request(HydratedField, [sources_field])[0] return cast(EagerFilesetWithSpec, field.value)
def test_build_file_forms(self) -> None: with self.workspace("a/b/c/BUILD") as root_dir: build_file = BuildFile(FileSystemProjectTree(root_dir), relpath="a/b/c/BUILD") self.assert_address("a/b/c", "c", BuildFileAddress(build_file=build_file)) self.assert_address( "a/b/c", "foo", BuildFileAddress(build_file=build_file, target_name="foo")) self.assertEqual( "a/b/c:foo", BuildFileAddress(build_file=build_file, target_name="foo").spec) with self.workspace("BUILD") as root_dir: build_file = BuildFile(FileSystemProjectTree(root_dir), relpath="BUILD") self.assert_address( "", "foo", BuildFileAddress(build_file=build_file, target_name="foo")) self.assertEqual( "//:foo", BuildFileAddress(build_file=build_file, target_name="foo").spec)
def __call__(self, *args, **kwargs): # Let the name default to the name of the directory the BUILD file is in (as long as it's # not the root directory), as this is a very common idiom. If there are multiple targets # in the BUILD file, we'll issue an error saying as much, and the author will have to name all # but one of them explicitly. if 'name' not in kwargs: dirname = os.path.basename(self._build_file.spec_path) if dirname: kwargs['name'] = dirname else: raise Addressable.AddressableInitError( 'Targets in root-level BUILD files must be named explicitly.') addressable = self._addressable_factory.capture(*args, **kwargs) addressable_name = addressable.addressed_name if addressable_name: address = BuildFileAddress(build_file=self._build_file, target_name=addressable_name) self._registration_callback(address, addressable) return addressable
async def run( console: Console, workspace: Workspace, runner: InteractiveRunner, build_root: BuildRoot, bfa: BuildFileAddress, options: RunOptions, ) -> Run: target = bfa.to_address() binary = await Get[CreatedBinary](Address, target) with temporary_dir(root_dir=str(Path(build_root.path, ".pants.d")), cleanup=True) as tmpdir: path_relative_to_build_root = str( Path(tmpdir).relative_to(build_root.path)) workspace.materialize_directory( DirectoryToMaterialize(binary.digest, path_prefix=path_relative_to_build_root)) console.write_stdout(f"Running target: {target}\n") full_path = str(Path(tmpdir, binary.binary_name)) run_request = InteractiveProcessRequest( argv=(full_path, *options.values.args), run_in_workspace=True, ) try: result = runner.run_local_interactive_process(run_request) exit_code = result.process_exit_code if result.process_exit_code == 0: console.write_stdout(f"{target} ran successfully.\n") else: console.write_stderr( f"{target} failed with code {result.process_exit_code}!\n") except Exception as e: console.write_stderr( f"Exception when attempting to run {target} : {e}\n") exit_code = -1 return Run(exit_code)