diff --git a/src/python/pants/engine/internals/parser.py b/src/python/pants/engine/internals/parser.py index cba242fd533..6e52d286085 100644 --- a/src/python/pants/engine/internals/parser.py +++ b/src/python/pants/engine/internals/parser.py @@ -26,7 +26,7 @@ from pants.engine.env_vars import EnvironmentVars from pants.engine.internals.defaults import BuildFileDefaultsParserState, SetDefaultsT from pants.engine.internals.dep_rules import BuildFileDependencyRulesParserState -from pants.engine.internals.target_adaptor import TargetAdaptor +from pants.engine.internals.target_adaptor import TargetAdaptor, SyntheticTargetAdaptor from pants.engine.target import Field, ImmutableValue, RegisteredTargetTypes from pants.engine.unions import UnionMembership from pants.util.docutil import doc_url @@ -334,7 +334,10 @@ def __call__(self, **kwargs: Any) -> TargetAdaptor: kwargs["__description_of_origin__"] = f"{self._parse_state.filepath()}:{source_line}" raw_values = dict(self._parse_state.defaults.get(self._type_alias)) raw_values.update(kwargs) - target_adaptor = TargetAdaptor(self._type_alias, **raw_values) + if not raw_values.pop("_extend_synthetic", False): + target_adaptor = TargetAdaptor(self._type_alias, **raw_values) + else: + target_adaptor = SyntheticTargetAdaptor(self._type_alias, **raw_values) self._parse_state.add(target_adaptor) return target_adaptor diff --git a/src/python/pants/engine/internals/synthetic_targets.py b/src/python/pants/engine/internals/synthetic_targets.py index 0a773f33c58..8b9bb48e9a8 100644 --- a/src/python/pants/engine/internals/synthetic_targets.py +++ b/src/python/pants/engine/internals/synthetic_targets.py @@ -154,7 +154,7 @@ def rules(cls) -> Iterator[UnionRule]: class SyntheticAddressMap(AddressMap): def process_declared_targets(self, address_map: AddressMap) -> None: for name, target_adaptor in address_map.name_to_target_adaptor.items(): - extend_synthetic = target_adaptor.kwargs.pop("_extend_synthetic", False) + extend_synthetic = isinstance(target_adaptor, SyntheticTargetAdaptor) if name not in self.name_to_target_adaptor: if extend_synthetic: raise InvalidTargetException( @@ -169,7 +169,7 @@ def process_declared_targets(self, address_map: AddressMap) -> None: # Pop synthetic target to let the explicit target declared in BUILD file take # precedence. - synthetic_target_adaptor = self.name_to_target_adaptor.pop(name) + synthetic_target_adaptor = self.name_to_target_adaptor.pop(name) # TODO: this here is another mutating operation! if not extend_synthetic: # The explicitly declared target should replace the synthetic one. diff --git a/src/python/pants/engine/internals/target_adaptor.py b/src/python/pants/engine/internals/target_adaptor.py index 2ecf485ba3a..17355cdcaa1 100644 --- a/src/python/pants/engine/internals/target_adaptor.py +++ b/src/python/pants/engine/internals/target_adaptor.py @@ -87,7 +87,7 @@ def debug_hint(self) -> str: return self.address.spec -@final +# @final -- we mark this class final further down. class TargetAdaptor: """A light-weight object to store target information before being converted into the Target API.""" @@ -124,3 +124,12 @@ def __eq__(self, other: Any | TargetAdaptor) -> bool: @property def name_explicitly_set(self) -> bool: return self.name is not None + + +@final +class SyntheticTargetAdaptor(TargetAdaptor): + """Subclass to indicate this target is used to extend a synthetic target only.""" + + +TargetAdaptor = final(TargetAdaptor) +