Skip to content
Merged
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
2 changes: 2 additions & 0 deletions tests/mocked_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class MockedEntryPoint:
class MockedPluginA(PluginType):
namespace = "test_namespace" # pyright: ignore[reportAssignmentType,reportIncompatibleMethodOverride]

is_build_plugin = True

def get_all_configs(self) -> list[VariantFeatureConfigType]:
return [
VariantFeatureConfig("name1", ["val1a", "val1b", "val1c", "val1d"]),
Expand Down
30 changes: 30 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from variantlib.constants import VARIANTS_JSON_SCHEMA_URL
from variantlib.constants import VARIANTS_JSON_VARIANT_DATA_KEY
from variantlib.constants import VariantsJsonDict
from variantlib.errors import PluginError
from variantlib.errors import ValidationError
from variantlib.models import provider as pconfig
from variantlib.models import variant as vconfig
Expand Down Expand Up @@ -764,3 +765,32 @@ def test_make_variant_dist_info_invalid_build_plugin() -> None:
variant_info=vinfo,
expand_build_plugin_properties=True,
)


def test_make_variant_dist_info_really_invalid_build_plugin() -> None:
vdesc = VariantDescription(
[
VariantProperty("second_namespace", "name3", "val3a"),
]
)
plugin_api = "tests.mocked_plugins:MockedPluginB"
vinfo = VariantInfo(
namespace_priorities=["second_namespace"],
providers={
"second_namespace": ProviderInfo(
plugin_api=plugin_api,
plugin_use=PluginUse.BUILD,
)
},
)

with pytest.raises(
PluginError,
match=r"Providers for namespaces {'second_namespace'} do not provide fixed "
r"supported configs, they cannot be used with plugin-use = 'build'",
):
make_variant_dist_info(
vdesc,
variant_info=vinfo,
expand_build_plugin_properties=True,
)
4 changes: 3 additions & 1 deletion variantlib/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,9 @@ def make_variant_dist_info(
filter_plugins=list(build_namespaces),
include_build_plugins=True,
) as plugin_loader:
configs = plugin_loader.get_supported_configs().values()
configs = plugin_loader.get_supported_configs(
require_fixed=True
).values()

for config in configs:
if config.namespace not in build_namespaces:
Expand Down
17 changes: 17 additions & 0 deletions variantlib/plugins/_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,27 @@ def main() -> int:
help="Load specified plugin API",
required=True,
)
parser.add_argument(
"--require-fixed",
action="store_true",
help="Require all plugins to provide fixed supported configs",
)
args = parser.parse_args()
commands = json.load(sys.stdin)
plugins = dict(zip(args.plugin_api, load_plugins(args.plugin_api), strict=True))

if args.require_fixed:
non_fixed_plugins = {
plugin.namespace
for plugin in plugins.values()
if not getattr(plugin, "is_build_plugin", False)
}
if non_fixed_plugins:
raise TypeError(
f"Providers for namespaces {non_fixed_plugins} do not provide fixed "
f"supported configs, they cannot be used with plugin-use = 'build'"
)

retval: dict[str, Any] = {}
for command, command_args in commands.items():
if command == "namespaces":
Expand Down
20 changes: 16 additions & 4 deletions variantlib/plugins/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ def __exit__(
self._namespace_map = None

def _call_subprocess(
self, plugin_apis: list[str], commands: dict[str, Any]
self,
plugin_apis: list[str],
commands: dict[str, Any],
args: Collection[str] = (),
) -> dict[str, Any]:
with TemporaryDirectory(prefix="variantlib") as temp_dir:
# Copy `variantlib/plugins/loader.py` into the temp_dir
Expand All @@ -110,7 +113,7 @@ def _call_subprocess(
).read_bytes()
)

args = []
args = [*args]
for plugin_api in plugin_apis:
args += ["--plugin-api", plugin_api]

Expand Down Expand Up @@ -184,6 +187,7 @@ def _get_configs(
self,
method: Literal["get_all_configs", "get_supported_configs"],
require_non_empty: bool,
require_fixed: bool,
) -> dict[str, ProviderConfig]:
self._check_plugins_loaded()
assert self._namespace_map is not None
Expand All @@ -208,6 +212,7 @@ def _get_configs(
configs = self._call_subprocess(
list(self._namespace_map.keys()),
{method: {}},
args=["--require-fixed"] if require_fixed else [],
)[method]

for plugin_api, plugin_configs in configs.items():
Expand All @@ -233,13 +238,20 @@ def get_all_configs(
self,
) -> dict[str, ProviderConfig]:
"""Get a mapping of namespaces to all valid configs"""
return self._get_configs("get_all_configs", require_non_empty=True)
return self._get_configs(
"get_all_configs", require_non_empty=True, require_fixed=False
)

def get_supported_configs(
self,
require_fixed: bool = False,
) -> dict[str, ProviderConfig]:
"""Get a mapping of namespaces to supported configs"""
return self._get_configs("get_supported_configs", require_non_empty=False)
return self._get_configs(
"get_supported_configs",
require_non_empty=False,
require_fixed=require_fixed,
)

@property
def plugin_api_values(self) -> dict[str, str]:
Expand Down
16 changes: 16 additions & 0 deletions variantlib/protocols.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,22 @@ def namespace(self) -> VariantNamespace:
"""Plugin namespace"""
raise NotImplementedError

@property
def is_build_plugin(self) -> bool:
"""
Is this plugin valid for `plugin-use = "build"`?

If this is True, then `get_supported_configs()` must always
return the same values, irrespective of the platform used.
This permits the plugin to be used with `plugin-use = "build"`,
where the supported properties are recorded at build time.

If the value of `get_supported_configs()` may change in any way
depending on the platform used, then it must be False
(the default).
"""
return False

@abstractmethod
def get_all_configs(self) -> list[VariantFeatureConfigType]:
"""Get all valid configs for the plugin"""
Expand Down
Loading