Skip to content

Commit f4ee329

Browse files
authored
fix: pull in fixes from scikit-build-core (#39)
Signed-off-by: Henry Schreiner <[email protected]>
1 parent 1377470 commit f4ee329

File tree

3 files changed

+91
-13
lines changed

3 files changed

+91
-13
lines changed

src/dynamic_metadata/info.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
from __future__ import annotations
22

3-
__all__ = ["ALL_FIELDS", "DICT_STR_FIELDS", "LIST_STR_FIELDS", "STR_FIELDS"]
4-
3+
__all__ = [
4+
"ALL_FIELDS",
5+
"DICT_STR_FIELDS",
6+
"LIST_DICT_FIELDS",
7+
"LIST_STR_FIELDS",
8+
"STR_FIELDS",
9+
]
510

611
# Name is not dynamically settable, so not in this list
712
STR_FIELDS = frozenset(
@@ -19,32 +24,36 @@
1924
"classifiers",
2025
"keywords",
2126
"dependencies",
22-
"license_files",
27+
"license-files",
2328
]
2429
)
2530

26-
2731
DICT_STR_FIELDS = frozenset(
2832
[
2933
"urls",
30-
"authors",
31-
"maintainers",
3234
"scripts",
3335
"gui-scripts",
34-
"entry-points",
3536
]
3637
)
3738

39+
LIST_DICT_FIELDS = frozenset(
40+
[
41+
"authors",
42+
"maintainers",
43+
]
44+
)
3845

3946
# "dynamic" and "name" can't be set or requested
4047
ALL_FIELDS = (
4148
STR_FIELDS
4249
| LIST_STR_FIELDS
4350
| DICT_STR_FIELDS
51+
| LIST_DICT_FIELDS
4452
| frozenset(
4553
[
4654
"optional-dependencies",
4755
"readme",
56+
"entry-points",
4857
]
4958
)
5059
)

src/dynamic_metadata/plugins/__init__.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
import typing
44
from collections.abc import Callable
55

6-
from ..info import DICT_STR_FIELDS, LIST_STR_FIELDS, STR_FIELDS
6+
from ..info import DICT_STR_FIELDS, LIST_DICT_FIELDS, LIST_STR_FIELDS, STR_FIELDS
77

8-
T = typing.TypeVar("T", bound="str | list[str] | dict[str, str] | dict[str, list[str]]")
8+
T = typing.TypeVar(
9+
"T",
10+
bound="str | list[str] | list[dict[str, str]] | dict[str, str] | dict[str, list[str]] | dict[str, dict[str, str]]",
11+
)
912

1013

1114
def _process_dynamic_metadata(field: str, action: Callable[[str], str], result: T) -> T:
1215
"""
13-
Helper function for processing the an action on the various possible metadata fields.
16+
Helper function for processing an action on the various possible metadata fields.
1417
"""
1518

1619
if field in STR_FIELDS:
@@ -29,7 +32,28 @@ def _process_dynamic_metadata(field: str, action: Callable[[str], str], result:
2932
):
3033
msg = f"Field {field!r} must be a dictionary of strings"
3134
raise RuntimeError(msg)
32-
return {k: action(v) for k, v in result.items()} # type: ignore[return-value]
35+
return {action(k): action(v) for k, v in result.items()} # type: ignore[return-value]
36+
if field in LIST_DICT_FIELDS:
37+
if not isinstance(result, list) or not all(
38+
isinstance(k, str) and isinstance(v, str)
39+
for d in result
40+
for k, v in d.items()
41+
):
42+
msg = f"Field {field!r} must be a dictionary of strings"
43+
raise RuntimeError(msg)
44+
return [{k: action(v) for k, v in d.items()} for d in result] # type: ignore[return-value]
45+
if field == "entry-points":
46+
if not isinstance(result, dict) or not all(
47+
isinstance(d, dict)
48+
and all(isinstance(k, str) and isinstance(v, str) for k, v in d.items())
49+
for d in result.values()
50+
):
51+
msg = "Field 'entry-points' must be a dictionary of dictionary of strings"
52+
raise RuntimeError(msg)
53+
return {
54+
dk: {action(k): action(v) for k, v in dv.items()}
55+
for dk, dv in result.items()
56+
} # type: ignore[return-value]
3357
if field == "optional-dependencies":
3458
if not isinstance(result, dict) or not all(
3559
isinstance(v, list) for v in result.values()

tests/test_package.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
from __future__ import annotations
22

3+
from typing import Any
4+
5+
import pytest
6+
37
import dynamic_metadata.loader
8+
import dynamic_metadata.plugins
49

510

611
def test_template_basic() -> None:
@@ -61,12 +66,16 @@ def test_template_entry_points() -> None:
6166
},
6267
"entry-points": {
6368
"provider": "dynamic_metadata.plugins.template",
64-
"result": {"my_point": "my_app:script_{project[version]}"},
69+
"result": {
70+
"my_group": {"my_point": "my_app:script_{project[version]}"}
71+
},
6572
},
6673
},
6774
)
6875

69-
assert pyproject["entry-points"] == {"my_point": "my_app:script_1.2.3"}
76+
assert pyproject["entry-points"] == {
77+
"my_group": {"my_point": "my_app:script_1.2.3"}
78+
}
7079

7180

7281
def test_regex() -> None:
@@ -87,3 +96,39 @@ def test_regex() -> None:
8796
)
8897

8998
assert pyproject["requires-python"] == ">=dynamic-metadata"
99+
100+
101+
@pytest.mark.parametrize(
102+
("field", "input", "output"),
103+
[
104+
pytest.param("version", "{sub}", "42", id="str"),
105+
pytest.param("classifiers", ["a", "{sub}"], ["a", "42"], id="list-str"),
106+
pytest.param(
107+
"scripts",
108+
{"a": "{sub}", "{sub}": "b"},
109+
{"a": "42", "42": "b"},
110+
id="dict-str",
111+
),
112+
pytest.param(
113+
"authors", [{"name": "{sub}"}], [{"name": "42"}], id="list-dict-str"
114+
),
115+
pytest.param(
116+
"optional-dependencies",
117+
{"dev": ["{sub}"]},
118+
{"dev": ["42"]},
119+
id="dict-list-str",
120+
),
121+
pytest.param("readme", {"text": "{sub}"}, {"text": "42"}, id="readme"),
122+
pytest.param(
123+
"entry-points",
124+
{"ep": {"{sub}": "{sub}"}},
125+
{"ep": {"42": "42"}},
126+
id="dict-dict-str",
127+
),
128+
],
129+
)
130+
def test_actions(field: str, input: Any, output: Any) -> None:
131+
result = dynamic_metadata.plugins._process_dynamic_metadata(
132+
field, lambda x: x.format(sub=42), input
133+
)
134+
assert output == result

0 commit comments

Comments
 (0)