Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,3 +420,4 @@ Created as part of the release process.
+ yjabri
+ Éric Jacob
+ Ólafur Páll Geirsson
+ Shubham Kumar Barnwal
19 changes: 19 additions & 0 deletions src/python/pants/engine/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,25 @@ def validate(self) -> None:
context. If the validation only makes sense for certain goals acting on targets; those
validations should be done in the associated rules.
"""
sources_field_types = tuple(
ft for ft in self.field_types if issubclass(ft, SourcesField)
)
if len(sources_field_types) > 1:
field_desc = ", ".join(
f"`{ft.alias}` ({ft.__name__})"
for ft in sorted(sources_field_types, key=lambda f: f.alias)
)
raise InvalidFieldException(
softwrap(
f"""
The {self.alias} target at {self.address} has multiple fields that subclass
`SourcesField`, which is not supported: APIs such as `tgt.get(SourcesField)`
require at most one. Found: {field_desc}.

Remove or consolidate the extra sources field(s).
"""
)
)


def _validate_origin_sources_blocks(origin_sources_blocks: FrozenDict[str, SourceBlocks]) -> None:
Expand Down
24 changes: 24 additions & 0 deletions src/python/pants/engine/target_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,30 @@ def test_target_validate() -> None:
FortranTarget({FortranVersion.alias: "bad"}, Address("", target_name="t"))


def test_target_rejects_multiple_sources_fields() -> None:
"""Regression test for https://github.com/pantsbuild/pants/issues/17132."""

class PrimarySources(MultipleSourcesField):
alias = "sources"
default = ("*.ext",)

class ExtraSources(MultipleSourcesField):
alias = "extra_sources"

class InvalidMultiSourcesTarget(Target):
alias = "invalid_multi_sources"
core_fields = (FortranVersion, PrimarySources, ExtraSources)

with pytest.raises(InvalidTargetException) as exc:
InvalidMultiSourcesTarget(
{FortranVersion.alias: "v1", PrimarySources.alias: ["a.ext"], ExtraSources.alias: ["b"]},
Address("", target_name="t"),
)
assert "multiple fields that subclass `SourcesField`" in str(exc.value)
assert "`extra_sources`" in str(exc.value)
assert "`sources`" in str(exc.value)


def test_target_residence_dir() -> None:
assert FortranTarget({}, Address("some_dir/subdir")).residence_dir == "some_dir/subdir"
assert (
Expand Down