Example #1
0
async def addresses_with_origins_from_filesystem_specs(
    filesystem_specs: FilesystemSpecs,
    global_options: GlobalOptions,
) -> AddressesWithOrigins:
    """Find the owner(s) for each FilesystemSpec while preserving the original FilesystemSpec those
  owners come from.
  """
    pathglobs_per_include = (filesystem_specs.path_globs_for_spec(spec)
                             for spec in filesystem_specs.includes)
    snapshot_per_include = await MultiGet(Get[Snapshot](PathGlobs, pg)
                                          for pg in pathglobs_per_include)
    owners_per_include = await MultiGet(
        Get[Owners](OwnersRequest(sources=snapshot.files))
        for snapshot in snapshot_per_include)
    result: List[AddressWithOrigin] = []
    for spec, owners in zip(filesystem_specs.includes, owners_per_include):
        if (global_options.owners_not_found_behavior !=
                OwnersNotFoundBehavior.ignore
                and isinstance(spec, FilesystemLiteralSpec)
                and not owners.addresses):
            file_path = PurePath(spec.to_spec_string())
            msg = (
                f"No owning targets could be found for the file `{file_path}`.\n\nPlease check "
                f"that there is a BUILD file in `{file_path.parent}` with a target whose `sources` field "
                f"includes `{file_path}`. See https://www.pantsbuild.org/build_files.html."
            )
            if global_options.owners_not_found_behavior == OwnersNotFoundBehavior.warn:
                logger.warning(msg)
            else:
                raise ResolveError(msg)
        result.extend(
            AddressWithOrigin(address=bfa, origin=spec)
            for bfa in owners.addresses)
    return AddressesWithOrigins(result)
Example #2
0
 def make_addresses_with_origins(
         *addresses: Address) -> AddressesWithOrigins:
     return AddressesWithOrigins([
         AddressWithOrigin(
             address=address,
             origin=SingleAddress(directory=address.spec_path,
                                  name=address.target_name),
         ) for address in addresses
     ])
Example #3
0
async def addresses_with_origins_from_address_families(
    address_mapper: AddressMapper,
    address_specs: AddressSpecs,
) -> AddressesWithOrigins:
    """Given an AddressMapper and list of AddressSpecs, return matching AddressesWithOrigins.

    :raises: :class:`ResolveError` if:
       - there were no matching AddressFamilies, or
       - the AddressSpec matches no addresses for SingleAddresses.
    :raises: :class:`AddressLookupError` if no targets are matched for non-SingleAddress specs.
    """
    # Capture a Snapshot covering all paths for these AddressSpecs, then group by directory.
    snapshot = await Get[Snapshot](PathGlobs,
                                   _address_spec_to_globs(
                                       address_mapper, address_specs))
    dirnames = {os.path.dirname(f) for f in snapshot.files}
    address_families = await MultiGet(Get[AddressFamily](Dir(d))
                                      for d in dirnames)
    address_family_by_directory = {af.namespace: af for af in address_families}

    matched_addresses: OrderedSet[Address] = OrderedSet()
    addr_to_origin: Dict[Address, AddressSpec] = {}

    for address_spec in address_specs:
        # NB: if an address spec is provided which expands to some number of targets, but those targets
        # match --exclude-target-regexp, we do NOT fail! This is why we wait to apply the tag and
        # exclude patterns until we gather all the targets the address spec would have matched
        # without them.
        try:
            addr_families_for_spec = address_spec.matching_address_families(
                address_family_by_directory)
        except AddressSpec.AddressFamilyResolutionError as e:
            raise ResolveError(e) from e

        try:
            all_bfaddr_tgt_pairs = address_spec.address_target_pairs_from_address_families(
                addr_families_for_spec)
            for bfaddr, _ in all_bfaddr_tgt_pairs:
                addr = bfaddr.to_address()
                # A target might be covered by multiple specs, so we take the most specific one.
                addr_to_origin[addr] = more_specific(addr_to_origin.get(addr),
                                                     address_spec)
        except AddressSpec.AddressResolutionError as e:
            raise AddressLookupError(e) from e
        except SingleAddress._SingleAddressResolutionError as e:
            _raise_did_you_mean(e.single_address_family, e.name, source=e)

        matched_addresses.update(
            bfaddr.to_address() for (bfaddr, tgt) in all_bfaddr_tgt_pairs
            if address_specs.matcher.matches_target_address_pair(bfaddr, tgt))

    # NB: This may be empty, as the result of filtering by tag and exclude patterns!
    return AddressesWithOrigins(
        AddressWithOrigin(address=addr, origin=addr_to_origin[addr])
        for addr in matched_addresses)
Example #4
0
async def addresses_with_origins_from_filesystem_specs(
    filesystem_specs: FilesystemSpecs,
    global_options: GlobalOptions,
) -> AddressesWithOrigins:
    """Find the owner(s) for each FilesystemSpec while preserving the original FilesystemSpec those
    owners come from.

    This will merge FilesystemSpecs that come from the same owning target into a single
    FilesystemMergedSpec.
    """
    pathglobs_per_include = (filesystem_specs.path_globs_for_spec(spec)
                             for spec in filesystem_specs.includes)
    snapshot_per_include = await MultiGet(Get[Snapshot](PathGlobs, pg)
                                          for pg in pathglobs_per_include)
    owners_per_include = await MultiGet(
        Get[Owners](OwnersRequest(sources=snapshot.files))
        for snapshot in snapshot_per_include)
    addresses_to_specs: DefaultDict[
        Address, List[Union[FilesystemLiteralSpec,
                            FilesystemResolvedGlobSpec]]] = defaultdict(list)
    for spec, snapshot, owners in zip(filesystem_specs.includes,
                                      snapshot_per_include,
                                      owners_per_include):
        if (global_options.options.owners_not_found_behavior !=
                OwnersNotFoundBehavior.ignore
                and isinstance(spec, FilesystemLiteralSpec)
                and not owners.addresses):
            file_path = PurePath(spec.to_spec_string())
            msg = (
                f"No owning targets could be found for the file `{file_path}`.\n\nPlease check "
                f"that there is a BUILD file in `{file_path.parent}` with a target whose `sources` field "
                f"includes `{file_path}`. See https://www.pantsbuild.org/build_files.html."
            )
            if global_options.options.owners_not_found_behavior == OwnersNotFoundBehavior.warn:
                logger.warning(msg)
            else:
                raise ResolveError(msg)
        # We preserve what literal files any globs resolved to. This allows downstream goals to be
        # more precise in which files they operate on.
        origin: Union[FilesystemLiteralSpec, FilesystemResolvedGlobSpec] = (
            spec if isinstance(spec, FilesystemLiteralSpec) else
            FilesystemResolvedGlobSpec(glob=spec.glob, files=snapshot.files))
        for address in owners.addresses:
            addresses_to_specs[address].append(origin)
    return AddressesWithOrigins(
        AddressWithOrigin(
            address, specs[0] if len(specs) ==
            1 else FilesystemMergedSpec.create(specs))
        for address, specs in addresses_to_specs.items())
Example #5
0
async def run_tests(
    console: Console,
    options: TestOptions,
    runner: InteractiveRunner,
    addresses_with_origins: AddressesWithOrigins,
    workspace: Workspace,
) -> Test:
    if options.values.debug:
        address_with_origin = addresses_with_origins.expect_single()
        addr_debug_request = await Get[AddressAndDebugRequest](
            AddressWithOrigin, address_with_origin
        )
        result = runner.run_local_interactive_process(addr_debug_request.request.ipr)
        return Test(result.process_exit_code)

    results = await MultiGet(
        Get[AddressAndTestResult](AddressWithOrigin, address_with_origin)
        for address_with_origin in addresses_with_origins
    )

    if options.values.run_coverage:
        # TODO: consider warning if a user uses `--coverage` but the language backend does not
        # provide coverage support. This might be too chatty to be worth doing?
        results_with_coverage = [
            x
            for x in results
            if x.test_result is not None and x.test_result.coverage_data is not None
        ]
        coverage_data_collections = itertools.groupby(
            results_with_coverage,
            lambda address_and_test_result: address_and_test_result.test_result.coverage_data.batch_cls,  # type: ignore[union-attr]
        )

        coverage_reports = await MultiGet(
            Get[CoverageReport](
                CoverageDataBatch, coverage_batch_cls(tuple(addresses_and_test_results))  # type: ignore[call-arg]
            )
            for coverage_batch_cls, addresses_and_test_results in coverage_data_collections
        )
        for report in coverage_reports:
            workspace.materialize_directory(
                DirectoryToMaterialize(
                    report.result_digest, path_prefix=str(report.directory_to_materialize_to),
                )
            )
            console.print_stdout(f"Wrote coverage report to `{report.directory_to_materialize_to}`")

    did_any_fail = False
    filtered_results = [(x.address, x.test_result) for x in results if x.test_result is not None]
    for address, test_result in filtered_results:
        if test_result.status == Status.FAILURE:
            did_any_fail = True
        if test_result.stdout:
            console.write_stdout(f"{address.reference()} stdout:\n{test_result.stdout}\n")
        if test_result.stderr:
            # NB: we write to stdout, rather than to stderr, to avoid potential issues interleaving the
            # two streams.
            console.write_stdout(f"{address.reference()} stderr:\n{test_result.stderr}\n")

    console.write_stdout("\n")

    for address, test_result in filtered_results:
        console.print_stdout(f"{address.reference():80}.....{test_result.status.value:>10}")

    if did_any_fail:
        console.print_stderr(console.red("\nTests failed"))
        exit_code = PANTS_FAILED_EXIT_CODE
    else:
        exit_code = PANTS_SUCCEEDED_EXIT_CODE

    return Test(exit_code)