Skip to content

Commit 824019a

Browse files
bilkeLecrisUThenryiiipre-commit-ci[bot]
authored
fix: add scripts to _DICT_STR_FIELDS for dynamic metadata. (#1070)
Fixes issue described in #1047 (comment). --------- Signed-off-by: Henry Schreiner <[email protected]> Co-authored-by: Cristian Le <[email protected]> Co-authored-by: Henry Schreiner <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 491f8e4 commit 824019a

File tree

2 files changed

+75
-3
lines changed

2 files changed

+75
-3
lines changed

src/scikit_build_core/metadata/__init__.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@
3232
_DICT_STR_FIELDS = frozenset(
3333
[
3434
"urls",
35+
"scripts",
36+
"gui-scripts",
37+
]
38+
)
39+
40+
_LIST_DICT_FIELDS = frozenset(
41+
[
3542
"authors",
3643
"maintainers",
3744
]
@@ -42,20 +49,25 @@
4249
_STR_FIELDS
4350
| _LIST_STR_FIELDS
4451
| _DICT_STR_FIELDS
52+
| _LIST_DICT_FIELDS
4553
| frozenset(
4654
[
4755
"optional-dependencies",
4856
"readme",
57+
"entry-points",
4958
]
5059
)
5160
)
5261

53-
T = typing.TypeVar("T", bound="str | list[str] | dict[str, str] | dict[str, list[str]]")
62+
T = typing.TypeVar(
63+
"T",
64+
bound="str | list[str] | list[dict[str, str]] | dict[str, str] | dict[str, list[str]] | dict[str, dict[str, str]]",
65+
)
5466

5567

5668
def _process_dynamic_metadata(field: str, action: Callable[[str], str], result: T) -> T:
5769
"""
58-
Helper function for processing the an action on the various possible metadata fields.
70+
Helper function for processing an action on the various possible metadata fields.
5971
"""
6072

6173
if field in _STR_FIELDS:
@@ -74,7 +86,28 @@ def _process_dynamic_metadata(field: str, action: Callable[[str], str], result:
7486
):
7587
msg = f"Field {field!r} must be a dictionary of strings"
7688
raise RuntimeError(msg)
77-
return {k: action(v) for k, v in result.items()} # type: ignore[return-value]
89+
return {action(k): action(v) for k, v in result.items()} # type: ignore[return-value]
90+
if field in _LIST_DICT_FIELDS:
91+
if not isinstance(result, list) or not all(
92+
isinstance(k, str) and isinstance(v, str)
93+
for d in result
94+
for k, v in d.items()
95+
):
96+
msg = f"Field {field!r} must be a dictionary of strings"
97+
raise RuntimeError(msg)
98+
return [{k: action(v) for k, v in d.items()} for d in result] # type: ignore[return-value]
99+
if field == "entry-points":
100+
if not isinstance(result, dict) or not all(
101+
isinstance(d, dict)
102+
and all(isinstance(k, str) and isinstance(v, str) for k, v in d.items())
103+
for d in result.values()
104+
):
105+
msg = "Field 'entry-points' must be a dictionary of dictionary of strings"
106+
raise RuntimeError(msg)
107+
return {
108+
dk: {action(k): action(v) for k, v in dv.items()}
109+
for dk, dv in result.items()
110+
} # type: ignore[return-value]
78111
if field == "optional-dependencies":
79112
if not isinstance(result, dict) or not all(
80113
isinstance(v, list) for v in result.values()

tests/test_dynamic_metadata_unit.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
from typing import Any
2+
3+
import pytest
4+
15
from scikit_build_core.builder._load_provider import process_dynamic_metadata
6+
from scikit_build_core.metadata import _process_dynamic_metadata
27

38

49
def test_template_basic() -> None:
@@ -64,3 +69,37 @@ def test_regex() -> None:
6469
)
6570

6671
assert pyproject["requires-python"] == ">=scikit_build_core"
72+
73+
74+
@pytest.mark.parametrize(
75+
("field", "input", "output"),
76+
[
77+
pytest.param("version", "{sub}", "42", id="str"),
78+
pytest.param("classifiers", ["a", "{sub}"], ["a", "42"], id="list-str"),
79+
pytest.param(
80+
"scripts",
81+
{"a": "{sub}", "{sub}": "b"},
82+
{"a": "42", "42": "b"},
83+
id="dict-str",
84+
),
85+
pytest.param(
86+
"authors", [{"name": "{sub}"}], [{"name": "42"}], id="list-dict-str"
87+
),
88+
pytest.param(
89+
"optional-dependencies",
90+
{"dev": ["{sub}"]},
91+
{"dev": ["42"]},
92+
id="dict-list-str",
93+
),
94+
pytest.param("readme", {"text": "{sub}"}, {"text": "42"}, id="readme"),
95+
pytest.param(
96+
"entry-points",
97+
{"ep": {"{sub}": "{sub}"}},
98+
{"ep": {"42": "42"}},
99+
id="dict-dict-str",
100+
),
101+
],
102+
)
103+
def test_actions(field: str, input: Any, output: Any) -> None:
104+
result = _process_dynamic_metadata(field, lambda x: x.format(sub=42), input)
105+
assert output == result

0 commit comments

Comments
 (0)