Skip to content

Commit

Permalink
Expose target field defaults in BUILD files. (pantsbuild#17649)
Browse files Browse the repository at this point in the history
  • Loading branch information
kaos authored Dec 6, 2022
1 parent 20c8e11 commit 818233a
Showing 5 changed files with 91 additions and 14 deletions.
34 changes: 30 additions & 4 deletions src/python/pants/engine/internals/build_files_test.py
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@

from pants.build_graph.address import BuildFileAddressRequest, MaybeAddress, ResolveError
from pants.build_graph.build_file_aliases import BuildFileAliases
from pants.core.target_types import GenericTarget
from pants.engine.addresses import Address, AddressInput, BuildFileAddress
from pants.engine.fs import DigestContents, FileContent, PathGlobs
from pants.engine.internals.build_files import (
@@ -56,7 +57,8 @@ def test_parse_address_family_empty() -> None:
rule_args=[
Parser(
build_root="",
target_type_aliases=[],
registered_target_types=RegisteredTargetTypes({}),
union_membership=UnionMembership({}),
object_aliases=BuildFileAliases(),
ignore_unrecognized_symbols=False,
),
@@ -99,7 +101,8 @@ def run_prelude_parsing_rule(prelude_content: str) -> BuildFilePreludeSymbols:
BuildFileOptions((), prelude_globs=("prelude",)),
Parser(
build_root="",
target_type_aliases=["target"],
registered_target_types=RegisteredTargetTypes({"target": GenericTarget}),
union_membership=UnionMembership({}),
object_aliases=BuildFileAliases(),
ignore_unrecognized_symbols=False,
),
@@ -180,7 +183,7 @@ class MockDepsField(Dependencies):


class MockMultipleSourcesField(MultipleSourcesField):
pass
default = ("*.mock",)


class MockTgt(Target):
@@ -376,6 +379,28 @@ def test_parametrize_defaults(target_adaptor_rule_runner: RuleRunner) -> None:
assert target_adaptor.kwargs["tags"] == ParametrizeDefault(a=("a", "root"), b=("non-root", "b"))


def test_augment_target_field_defaults(target_adaptor_rule_runner: RuleRunner) -> None:
target_adaptor_rule_runner.write_files(
{
"BUILD": dedent(
"""
mock_tgt(
sources=(
"*.added",
*mock_tgt.sources.default,
),
)
"""
),
},
)
target_adaptor = target_adaptor_rule_runner.request(
TargetAdaptor,
[TargetAdaptorRequest(Address(""), description_of_origin="tests")],
)
assert target_adaptor.kwargs["sources"] == ("*.added", "*.mock")


def test_target_adaptor_not_found(target_adaptor_rule_runner: RuleRunner) -> None:
with pytest.raises(ExecutionError) as exc:
target_adaptor_rule_runner.request(
@@ -428,7 +453,8 @@ def test_build_files_share_globals() -> None:
BuildFileOptions((), prelude_globs=("prelude",)),
Parser(
build_root="",
target_type_aliases=[],
registered_target_types=RegisteredTargetTypes({}),
union_membership=UnionMembership({}),
object_aliases=BuildFileAliases(),
ignore_unrecognized_symbols=False,
),
4 changes: 3 additions & 1 deletion src/python/pants/engine/internals/mapper_test.py
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
from pants.base.exceptions import MappingError
from pants.build_graph.address import Address
from pants.build_graph.build_file_aliases import BuildFileAliases
from pants.core.target_types import GenericTarget
from pants.engine.internals.defaults import BuildFileDefaults, BuildFileDefaultsParserState
from pants.engine.internals.mapper import (
AddressFamily,
@@ -31,7 +32,8 @@ def parse_address_map(build_file: str, *, ignore_unrecognized_symbols: bool = Fa
path = "/dev/null"
parser = Parser(
build_root="",
target_type_aliases=["thing"],
registered_target_types=RegisteredTargetTypes({"thing": GenericTarget}),
union_membership=UnionMembership({}),
object_aliases=BuildFileAliases(),
ignore_unrecognized_symbols=ignore_unrecognized_symbols,
)
50 changes: 45 additions & 5 deletions src/python/pants/engine/internals/parser.py
Original file line number Diff line number Diff line change
@@ -10,14 +10,17 @@
from difflib import get_close_matches
from io import StringIO
from pathlib import PurePath
from typing import Any, Iterable
from typing import Any

from pants.base.deprecated import warn_or_error
from pants.base.exceptions import MappingError
from pants.base.parse_context import ParseContext
from pants.build_graph.build_file_aliases import BuildFileAliases
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.target import Field, ImmutableValue, RegisteredTargetTypes
from pants.engine.unions import UnionMembership
from pants.util.docutil import doc_url
from pants.util.frozendict import FrozenDict
from pants.util.strutil import softwrap
@@ -94,25 +97,41 @@ def set_dependencies_rules(self, *args, **kwargs) -> None:
self._dependencies_rules.set_dependency_rules(self.filepath(), *args, **kwargs)


class RegistrarField:
__slots__ = ("_field_type",)

def __init__(self, field_type: type[Field]) -> None:
self._field_type = field_type

@property
def default(self) -> ImmutableValue:
return self._field_type.default


class Parser:
def __init__(
self,
*,
build_root: str,
target_type_aliases: Iterable[str],
registered_target_types: RegisteredTargetTypes,
union_membership: UnionMembership,
object_aliases: BuildFileAliases,
ignore_unrecognized_symbols: bool,
) -> None:
self._symbols, self._parse_state = self._generate_symbols(
build_root, target_type_aliases, object_aliases
build_root,
object_aliases,
registered_target_types,
union_membership,
)
self.ignore_unrecognized_symbols = ignore_unrecognized_symbols

@staticmethod
def _generate_symbols(
build_root: str,
target_type_aliases: Iterable[str],
object_aliases: BuildFileAliases,
registered_target_types: RegisteredTargetTypes,
union_membership: UnionMembership,
) -> tuple[FrozenDict[str, Any], ParseState]:
# N.B.: We re-use the thread local ParseState across symbols for performance reasons.
# This allows a single construction of all symbols here that can be re-used for each BUILD
@@ -122,6 +141,27 @@ def _generate_symbols(
class Registrar:
def __init__(self, type_alias: str) -> None:
self._type_alias = type_alias
for field_type in registered_target_types.aliases_to_types[
type_alias
].class_field_types(union_membership):
registrar_field = RegistrarField(field_type)
setattr(self, field_type.alias, registrar_field)
if field_type.deprecated_alias:

def deprecated_field(self):
# TODO(17720) Support fixing automatically with `build-file` deprecation
# fixer.
warn_or_error(
removal_version=field_type.deprecated_alias_removal_version,
entity=f"the field name {field_type.deprecated_alias}",
hint=(
f"Instead, use `{type_alias}.{field_type.alias}`, which "
"behaves the same."
),
)
return registrar_field

setattr(self, field_type.deprecated_alias, property(deprecated_field))

def __str__(self) -> str:
"""The BuildFileDefaultsParserState.set_defaults() rely on string inputs.
@@ -154,7 +194,7 @@ def __call__(self, **kwargs: Any) -> TargetAdaptor:
"__dependents_rules__": parse_state.set_dependents_rules,
"__dependencies_rules__": parse_state.set_dependencies_rules,
}
symbols.update((alias, Registrar(alias)) for alias in target_type_aliases)
symbols.update((alias, Registrar(alias)) for alias in registered_target_types.aliases)

parse_context = ParseContext(
build_root=build_root, type_aliases=symbols, filepath_oracle=parse_state
14 changes: 11 additions & 3 deletions src/python/pants/engine/internals/parser_test.py
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
import pytest

from pants.build_graph.build_file_aliases import BuildFileAliases
from pants.core.target_types import GenericTarget
from pants.engine.internals.defaults import BuildFileDefaults, BuildFileDefaultsParserState
from pants.engine.internals.parser import (
BuildFilePreludeSymbols,
@@ -30,7 +31,8 @@ def defaults_parser_state() -> BuildFileDefaultsParserState:
def test_imports_banned(defaults_parser_state: BuildFileDefaultsParserState) -> None:
parser = Parser(
build_root="",
target_type_aliases=[],
registered_target_types=RegisteredTargetTypes({}),
union_membership=UnionMembership({}),
object_aliases=BuildFileAliases(),
ignore_unrecognized_symbols=False,
)
@@ -55,7 +57,10 @@ def test_unrecognized_symbol(defaults_parser_state: BuildFileDefaultsParserState
def perform_test(extra_targets: list[str], dym: str) -> None:
parser = Parser(
build_root="",
target_type_aliases=["tgt", *extra_targets],
registered_target_types=RegisteredTargetTypes(
{alias: GenericTarget for alias in ("tgt", *extra_targets)}
),
union_membership=UnionMembership({}),
object_aliases=build_file_aliases,
ignore_unrecognized_symbols=False,
)
@@ -83,7 +88,10 @@ def perform_test(extra_targets: list[str], dym: str) -> None:
with no_exception():
parser = Parser(
build_root="",
target_type_aliases=["tgt", *extra_targets],
registered_target_types=RegisteredTargetTypes(
{alias: GenericTarget for alias in ("tgt", *extra_targets)}
),
union_membership=UnionMembership({}),
object_aliases=build_file_aliases,
ignore_unrecognized_symbols=True,
)
3 changes: 2 additions & 1 deletion src/python/pants/init/engine_initializer.py
Original file line number Diff line number Diff line change
@@ -242,7 +242,8 @@ def setup_graph_extended(
def parser_singleton() -> Parser:
return Parser(
build_root=build_root_path,
target_type_aliases=registered_target_types.aliases,
registered_target_types=registered_target_types,
union_membership=union_membership,
object_aliases=build_configuration.registered_aliases,
ignore_unrecognized_symbols=ignore_unrecognized_build_file_symbols,
)

0 comments on commit 818233a

Please sign in to comment.