Skip to content

Commit 368e990

Browse files
committed
Add an API to describe variant using labels
Add a minimal API that lets plugins process the variant description and add short labels that could be used to describe the wheel. The labels are entirely optional -- plugins may not return any, and clients may ignore them. The design also assumes that the client is responsible for choosing how many labels to use before truncating -- though I suppose I'll add a helper function to `variantlib` for that purpose. The design assumes that plugin get complete unfiltered `VariantDescription` -- and therefore also see variant metadata from other plugins. I don't think that's really a problem, though it assumes that the plugin must compare both against provider and key names. At this point, the protocol makes the API mandatory, even if it would only return an empty list unconditionally. Perhaps we should make the function optional somehow instead.
1 parent cca81ad commit 368e990

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

tests/test_plugins.py

+52
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from variantlib.base import PluginBase
77
from variantlib.config import KeyConfig, ProviderConfig
8+
from variantlib.meta import VariantDescription, VariantMeta
89
from variantlib.plugins import PluginLoader
910

1011

@@ -20,6 +21,12 @@ def get_supported_configs(self) -> Optional[ProviderConfig]:
2021
],
2122
)
2223

24+
def get_variant_labels(self, variant_desc: VariantDescription) -> list[str]:
25+
for meta in variant_desc:
26+
if meta.provider == self.__name__ and meta.key == "key1":
27+
return [meta.value.removeprefix("val")]
28+
return []
29+
2330

2431
# NB: this plugin deliberately does not inherit from PluginBase
2532
# to test that we don't rely on that inheritance
@@ -34,6 +41,11 @@ def get_supported_configs(self) -> Optional[ProviderConfig]:
3441
],
3542
)
3643

44+
def get_variant_labels(self, variant_desc: VariantDescription) -> list[str]:
45+
if VariantMeta(self.__name__, "key3", "val3a") in variant_desc:
46+
return ["sec"]
47+
return []
48+
3749

3850
class MockedPluginC(PluginBase):
3951
namespace = "incompatible_plugin"
@@ -48,6 +60,13 @@ class ClashingPlugin(PluginBase):
4860
def get_supported_configs(self) -> Optional[ProviderConfig]:
4961
return None
5062

63+
def get_variant_labels(self, variant_desc: VariantDescription) -> list[str]:
64+
ret = []
65+
for meta in variant_desc:
66+
if meta.provider == self.__name__ and meta.value == "on":
67+
ret.append(meta.key)
68+
return ret
69+
5170

5271
@dataclass
5372
class MockedDistribution:
@@ -137,3 +156,36 @@ def test_namespace_clash(mocker):
137156
assert "same namespace test_plugin" in str(exc)
138157
assert "test-plugin" in str(exc)
139158
assert "clashing-plugin" in str(exc)
159+
160+
161+
@pytest.mark.parametrize("variant_desc,expected",
162+
[
163+
(VariantDescription([
164+
VariantMeta("test_plugin", "key1", "val1a"),
165+
VariantMeta("test_plugin", "key2", "val2b"),
166+
VariantMeta("second_plugin", "key3", "val3a"),
167+
VariantMeta("other_plugin", "flag2", "on"),
168+
]), ["1a", "sec", "flag2"]),
169+
(VariantDescription([
170+
# note that VariantMetas don't actually have to be supported
171+
# by the system in question -- we could be cross-building
172+
# for another system
173+
VariantMeta("test_plugin", "key1", "val1f"),
174+
VariantMeta("test_plugin", "key2", "val2b"),
175+
VariantMeta("second_plugin", "key3", "val3a"),
176+
]), ["1f", "sec"]),
177+
(VariantDescription([
178+
VariantMeta("test_plugin", "key2", "val2b"),
179+
VariantMeta("second_plugin", "key3", "val3a"),
180+
]), ["sec"]),
181+
(VariantDescription([
182+
VariantMeta("test_plugin", "key2", "val2b"),
183+
]), []),
184+
(VariantDescription([
185+
VariantMeta("test_plugin", "key2", "val2b"),
186+
VariantMeta("other_plugin", "flag1", "on"),
187+
VariantMeta("other_plugin", "flag2", "on"),
188+
]), ["flag1", "flag2"]),
189+
])
190+
def test_get_variant_labels(mocked_plugin_loader, variant_desc, expected):
191+
assert mocked_plugin_loader.get_variant_labels(variant_desc) == expected

variantlib/base.py

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import Protocol, runtime_checkable
33

44
from variantlib.config import ProviderConfig
5+
from variantlib.meta import VariantDescription
56

67

78
@runtime_checkable
@@ -17,6 +18,10 @@ def get_supported_configs(self) -> ProviderConfig:
1718
"""Get supported configs for the current system"""
1819
...
1920

21+
def get_variant_labels(self, variant_desc: VariantDescription) -> list[str]:
22+
"""Get list of short labels to describe the variant"""
23+
...
24+
2025

2126
class PluginBase(ABC):
2227
"""An abstract base class that can be used to implement plugins"""
@@ -26,3 +31,6 @@ def namespace(self) -> str: ...
2631

2732
@abstractmethod
2833
def get_supported_configs(self) -> ProviderConfig: ...
34+
35+
def get_variant_labels(self, variant_desc: VariantDescription) -> list[str]:
36+
return []

variantlib/plugins.py

+9
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,12 @@ def get_dist_name_mapping(self) -> dict[str, str]:
9292
"""Get a mapping from plugin names to distribution names"""
9393

9494
return self._dist_names
95+
96+
def get_variant_labels(self, variant_desc: VariantDescription) -> list[str]:
97+
"""Get list of short labels to describe the variant"""
98+
99+
labels = []
100+
for plugin in self._plugins.values():
101+
labels += plugin.get_variant_labels(variant_desc)
102+
103+
return labels

0 commit comments

Comments
 (0)