Skip to content

Commit dc3777c

Browse files
Flit support for variants.json format + Multiple Props per NS/Feat
1 parent 08f3bd4 commit dc3777c

File tree

7 files changed

+266
-45
lines changed

7 files changed

+266
-45
lines changed

flit/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from .config import ConfigError
1313
from .log import enable_colourful_output
1414

15-
__version__ = '3.12.0'
15+
__version__ = '3.12.0+wheelnext'
1616

1717
log = logging.getLogger(__name__)
1818

flit/install.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from subprocess import check_call, check_output
1515
import sysconfig
1616

17+
from flit_core.variant_constants import VARIANT_DIST_INFO_FILENAME
1718
from flit_core import common
1819
from .config import read_flit_config
1920
from ._get_dirs import get_dirs
@@ -384,6 +385,10 @@ def write_dist_info(self, site_pkgs):
384385
metadata.write_metadata_file(f)
385386
self.installed_files.append(dist_info / 'METADATA')
386387

388+
with open(osp.join(dist_info, VARIANT_DIST_INFO_FILENAME), 'w', encoding='utf-8') as f:
389+
metadata.write_variants_json_file(f)
390+
self.installed_files.append(dist_info / VARIANT_DIST_INFO_FILENAME)
391+
387392
with (dist_info / 'INSTALLER').open('w', encoding='utf-8') as f:
388393
f.write('flit')
389394
self.installed_files.append(dist_info / 'INSTALLER')

flit_core/flit_core/buildapi.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .config import read_flit_config
1212
from .wheel import make_wheel_in, _write_wheel_file
1313
from .sdist import SdistBuilder
14+
from .variant_constants import VARIANT_DIST_INFO_FILENAME
1415

1516
log = logging.getLogger(__name__)
1617

@@ -57,6 +58,9 @@ def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
5758
with open(osp.join(dist_info, 'METADATA'), 'w', encoding='utf-8') as f:
5859
metadata.write_metadata_file(f)
5960

61+
with open(osp.join(dist_info, VARIANT_DIST_INFO_FILENAME), 'w', encoding='utf-8') as f:
62+
metadata.write_variants_json_file(f)
63+
6064
if ini_info.entrypoints:
6165
with open(osp.join(dist_info, 'entry_points.txt'), 'w', encoding='utf-8') as f:
6266
write_entry_points(ini_info.entrypoints, f)

flit_core/flit_core/common.py

Lines changed: 93 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
11
import ast
22
from contextlib import contextmanager
3+
from collections import defaultdict
34
import hashlib
45
import logging
56
import os
67
import sys
8+
import json
9+
from typing import Any
710

811
from pathlib import Path
912
import re
1013

1114
log = logging.getLogger(__name__)
1215

16+
from .variant_constants import (
17+
VALIDATION_PROPERTY_REGEX,
18+
VARIANTS_JSON_SCHEMA_KEY,
19+
VARIANTS_JSON_SCHEMA_URL,
20+
VARIANTS_JSON_VARIANT_DATA_KEY,
21+
VARIANT_INFO_DEFAULT_PRIO_KEY,
22+
VARIANT_INFO_FEATURE_KEY,
23+
VARIANT_INFO_NAMESPACE_KEY,
24+
VARIANT_INFO_PROPERTY_KEY,
25+
VARIANT_INFO_PROVIDER_DATA_KEY,
26+
VARIANT_INFO_PROVIDER_PLUGIN_API_KEY,
27+
VARIANT_INFO_PROVIDER_ENABLE_IF_KEY,
28+
VARIANT_INFO_PROVIDER_REQUIRES_KEY
29+
)
1330
from .versionno import normalise_version
1431

1532
class Module:
@@ -351,13 +368,14 @@ class Metadata:
351368
license_files = ()
352369
dynamic = ()
353370

354-
variant_hash = None
355-
variant_properties = ()
356-
variant_requires = ()
357-
variant_plugin_apis = ()
358-
variant_default_namespace_priorities = ()
359-
variant_default_feature_priorities = ()
360-
variant_default_property_priorities = ()
371+
variant_hash: str | None = None
372+
variant_properties: list[str] = []
373+
variant_plugins: dict[str, dict[str, list[str] | str]] = {}
374+
variant_default_priorities: dict[str, Any] = {
375+
"namespace": [],
376+
"feature": {},
377+
"property": {}
378+
}
361379

362380
metadata_version = "2.4"
363381

@@ -452,26 +470,74 @@ def write_metadata_file(self, fp):
452470
if self.description is not None:
453471
fp.write('\n' + self.description + '\n')
454472

473+
474+
def write_variants_json_file(self, fp):
475+
"""Write out Variant Metadata in json format"""
476+
455477
if self.variant_hash is not None:
456-
fp.write('Variant-hash: {}\n'.format(self.variant_hash))
457-
for vprop in self.variant_properties:
458-
fp.write('Variant-property: {}\n'.format(vprop))
459-
for vreq in self.variant_requires:
460-
fp.write('Variant-requires: {}\n'.format(vreq))
461-
for vAPI in self.variant_plugin_apis:
462-
fp.write('Variant-plugin-api: {}\n'.format(vAPI))
463-
if self.variant_default_namespace_priorities:
464-
fp.write('Variant-default-namespace-priorities: {}\n'.format(
465-
', '.join(self.variant_default_namespace_priorities)
466-
))
467-
if self.variant_default_feature_priorities:
468-
fp.write('Variant-default-feature-priorities: {}\n'.format(
469-
', '.join(self.variant_default_feature_priorities)
470-
))
471-
if self.variant_default_property_priorities:
472-
fp.write('Variant-default-property-priorities: {}\n'.format(
473-
', '.join(self.variant_default_property_priorities)
474-
))
478+
data = {
479+
VARIANTS_JSON_SCHEMA_KEY: VARIANTS_JSON_SCHEMA_URL,
480+
VARIANT_INFO_DEFAULT_PRIO_KEY: {},
481+
VARIANT_INFO_PROVIDER_DATA_KEY: {},
482+
VARIANTS_JSON_VARIANT_DATA_KEY: {}
483+
}
484+
485+
# ==================== VARIANT_INFO_DEFAULT_PRIO_KEY ==================== #
486+
487+
if (ns_prio := self.variant_default_priorities["namespace"]):
488+
data[VARIANT_INFO_DEFAULT_PRIO_KEY][VARIANT_INFO_NAMESPACE_KEY] = ns_prio
489+
490+
if (feat_prio := self.variant_default_priorities["feature"]):
491+
data[VARIANT_INFO_DEFAULT_PRIO_KEY][VARIANT_INFO_FEATURE_KEY] = feat_prio
492+
493+
if (prop_prio := self.variant_default_priorities["property"]):
494+
data[VARIANT_INFO_DEFAULT_PRIO_KEY][VARIANT_INFO_PROPERTY_KEY] = prop_prio
495+
496+
if not data[VARIANT_INFO_DEFAULT_PRIO_KEY]:
497+
# If no default priorities are set, remove the key
498+
del data[VARIANT_INFO_DEFAULT_PRIO_KEY]
499+
500+
# ==================== VARIANT_INFO_PROVIDER_DATA_KEY ==================== #
501+
502+
variant_providers = defaultdict(dict)
503+
for ns, plugin_conf in self.variant_plugins.items():
504+
variant_providers[ns][VARIANT_INFO_PROVIDER_REQUIRES_KEY] = plugin_conf.get("requires", [])
505+
506+
if (enable_if := plugin_conf.get("enable_if", None)) is not None:
507+
variant_providers[ns][VARIANT_INFO_PROVIDER_ENABLE_IF_KEY] = enable_if
508+
509+
if (plugin_api := plugin_conf.get("plugin_api", None)) is not None:
510+
variant_providers[ns][VARIANT_INFO_PROVIDER_PLUGIN_API_KEY] = plugin_api
511+
512+
data[VARIANT_INFO_PROVIDER_DATA_KEY] = variant_providers
513+
514+
# ==================== VARIANTS_JSON_VARIANT_DATA_KEY ==================== #
515+
516+
variant_data = defaultdict(lambda: defaultdict(set))
517+
for vprop_str in self.variant_properties:
518+
match = VALIDATION_PROPERTY_REGEX.match(vprop_str)
519+
if not match:
520+
raise ValueError(
521+
f"Invalid variant property '{vprop_str}' in variant {self.variant_hash}"
522+
)
523+
namespace = match.group('namespace')
524+
feature = match.group('feature')
525+
value = match.group('value')
526+
variant_data[namespace][feature].add(value)
527+
data[VARIANTS_JSON_VARIANT_DATA_KEY][self.variant_hash] = variant_data
528+
529+
def preprocess(data):
530+
"""Preprocess the data to ensure it is JSON serializable."""
531+
if isinstance(data, (defaultdict, dict)):
532+
return {k: preprocess(v) for k, v in data.items()}
533+
if isinstance(data, set):
534+
return list(data)
535+
return data
536+
537+
json.dump(
538+
preprocess(data), fp, indent=4, sort_keys=True, ensure_ascii=False
539+
)
540+
475541

476542
@property
477543
def supports_py2(self):
@@ -488,7 +554,7 @@ def make_metadata(module, ini_info):
488554
md_dict.update(ini_info.metadata)
489555
vconfig = getattr(ini_info, "variant_config", None)
490556
if vconfig is not None:
491-
md_dict.update(vconfig.to_metadata_dict())
557+
md_dict.update(vconfig.to_variant_cfg_dict())
492558
return Metadata(md_dict)
493559

494560

flit_core/flit_core/config.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,8 @@ def add_scripts(self, scripts_dict):
287287
@dataclass
288288
class VariantProviderConfig:
289289
requires: list[str]
290-
plugin_api: str
290+
plugin_api: str | None = None
291+
enable_if: str | None = None
291292

292293
@classmethod
293294
def from_dict(cls, data: dict):
@@ -302,8 +303,6 @@ def validate(self):
302303
"""Validates the VariantProviderConfig instance."""
303304
if not self.requires:
304305
raise ValueError("Requires list cannot be empty")
305-
if not self.plugin_api:
306-
raise ValueError("Plugin-API cannot be empty")
307306

308307

309308
@dataclass
@@ -365,25 +364,24 @@ def validate(self):
365364
for provider_cfg in self.providers.values():
366365
provider_cfg.validate()
367366

368-
def to_metadata_dict(self) -> dict[str, Any]:
367+
def to_variant_cfg_dict(self) -> dict[str, Any]:
369368
"""Converts the VariantConfig instance to a metadata dictionary."""
370369
return {
371370
"variant_hash": self.vhash,
372371
"variant_properties": self.properties,
373-
"variant_requires": [
374-
f"{namespace}: {preq}"
372+
"variant_plugins": {
373+
namespace: {
374+
"requires": provider_cfg.requires,
375+
"plugin_api": provider_cfg.plugin_api,
376+
"enable_if": provider_cfg.enable_if
377+
}
375378
for namespace, provider_cfg in self.providers.items()
376-
for preq in provider_cfg.requires
377-
],
378-
"variant_plugin_apis": [
379-
f"{namespace}: {provider_cfg.plugin_api}"
380-
for namespace, provider_cfg in self.providers.items()
381-
],
382-
"variant_default_namespace_priorities": self.default_priorities[
383-
"namespace"
384-
],
385-
"variant_default_feature_priorities": self.default_priorities["feature"],
386-
"variant_default_property_priorities": self.default_priorities["property"],
379+
},
380+
"variant_default_priorities": {
381+
"namespace": self.default_priorities.get("namespace", []),
382+
"feature": self.default_priorities.get("feature", {}),
383+
"property": self.default_priorities.get("property", {})
384+
}
387385
}
388386

389387

0 commit comments

Comments
 (0)