Skip to content

Commit 4f2baad

Browse files
authored
Merge pull request #7 from scikit-build/henryiii/feat/metadata2
refactor: directly specify entrypoints
2 parents a73ba08 + 276f9a8 commit 4f2baad

File tree

10 files changed

+104
-99
lines changed

10 files changed

+104
-99
lines changed

noxfile.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ def tests(session: nox.Session) -> None:
3838
Run the unit and regular tests.
3939
"""
4040
env = {"PIP_DISABLE_PIP_VERSION_CHECK": "1"}
41-
# add hatch-fancy-pypi-readme and setuptools-scm to extra to test the
42-
# dynamic metadata plugins
43-
extra = []
41+
extra = ["hatch-fancy-pypi-readme", "setuptools-scm"]
4442
# This will not work if system CMake is too old (<3.15)
4543
if shutil.which("cmake") is None and shutil.which("cmake3") is None:
4644
extra.append("cmake")

pyproject.toml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ dynamic = ["version"]
3333

3434
dependencies = [
3535
"exceptiongroup; python_version<'3.11'",
36-
"importlib-metadata >=4.13; python_version<'3.11'",
3736
"importlib_resources >=1.3; python_version<'3.9'",
3837
"packaging >=20.9",
3938
"tomli >=1.1; python_version<'3.11'",
@@ -89,10 +88,6 @@ Examples = "https://github.com/scikit-build/scikit-build-core/tree/main/tests/pa
8988
cmake_extensions = "scikit_build_core.setuptools.extension:cmake_extensions"
9089
cmake_source_dir = "scikit_build_core.setuptools.extension:cmake_source_dir"
9190

92-
[project.entry-points."scikit_build.metadata"]
93-
setuptools_scm = "scikit_build_core.settings.metadata:setuptools_scm_version"
94-
fancy_pypi_readme = "scikit_build_core.settings.metadata:fancy_pypi_readme"
95-
9691
[tool.hatch]
9792
version.source = "vcs"
9893
build.hooks.vcs.version-file = "src/scikit_build_core/_version.py"
@@ -194,7 +189,7 @@ select = [
194189
"UP", # pyupgrade
195190
"YTT", # flake8-2020
196191
]
197-
extend-ignore = ["PLR0915", "PLR0912", "PLR2004", "E501"]
192+
extend-ignore = ["PLR", "E501", "PT004"]
198193
target-version = "py37"
199194
typing-modules = ["scikit_build_core._compat.typing"]
200195
src = ["src"]

src/scikit_build_core/_compat/importlib.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,7 @@
77
else:
88
from importlib import resources
99

10-
if sys.version_info < (3, 11):
11-
import importlib_metadata as metadata
12-
else:
13-
from importlib import metadata
14-
15-
__all__ = ["metadata", "resources"]
10+
__all__ = ["resources"]
1611

1712

1813
def __dir__() -> list[str]:
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from __future__ import annotations
2+
3+
__all__: list[str] = []
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from __future__ import annotations
2+
3+
from typing import Any
4+
5+
__all__ = ["dynamic_metadata"]
6+
7+
8+
def __dir__() -> list[str]:
9+
return __all__
10+
11+
12+
def dynamic_metadata(
13+
pyproject_dict: dict[str, Any]
14+
) -> dict[str, str | dict[str, str | None]]:
15+
from hatch_fancy_pypi_readme._builder import build_text
16+
from hatch_fancy_pypi_readme._config import load_and_validate_config
17+
18+
config = load_and_validate_config(
19+
pyproject_dict["tool"]["hatch"]["metadata"]["hooks"]["fancy-pypi-readme"]
20+
)
21+
22+
return {
23+
"readme": {
24+
"content-type": config.content_type,
25+
"text": build_text(config.fragments, config.substitutions),
26+
}
27+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from __future__ import annotations
2+
3+
from pathlib import Path
4+
5+
__all__ = ["dynamic_metadata"]
6+
7+
8+
def __dir__() -> list[str]:
9+
return __all__
10+
11+
12+
def dynamic_metadata(
13+
pyproject_dict: dict[str, object] # noqa: ARG001
14+
) -> dict[str, str | dict[str, str | None]]:
15+
# this is a classic implementation, waiting for the release of
16+
# vcs-versioning and an improved public interface
17+
from setuptools_scm import Configuration, _get_version
18+
19+
config = Configuration.from_file(str(Path("pyproject.toml")))
20+
version: str = _get_version(config)
21+
22+
return {"version": version}

src/scikit_build_core/settings/metadata.py

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

3-
import warnings
4-
from pathlib import Path
3+
import importlib
54
from typing import Any
65

76
from pyproject_metadata import StandardMetadata
87

9-
from .._compat import importlib
108
from ..settings.skbuild_model import ScikitBuildSettings
119

12-
__all__ = ["setuptools_scm_version", "fancy_pypi_readme", "get_standard_metadata"]
10+
__all__ = ["get_standard_metadata"]
1311

1412

1513
def __dir__() -> list[str]:
1614
return __all__
1715

1816

19-
def setuptools_scm_version(
20-
_pyproject_dict: dict[str, Any]
21-
) -> dict[str, str | dict[str, str | None]]:
22-
# this is a placeholder version of this function, waiting for the release
23-
# of vcs-versioning and an improved public interface
24-
from setuptools_scm import Configuration, _get_version
25-
26-
config = Configuration.from_file(str(Path("pyproject.toml")))
27-
version: str = _get_version(config)
28-
29-
return {"version": version}
30-
31-
32-
def fancy_pypi_readme(
33-
pyproject_dict: dict[str, Any]
34-
) -> dict[str, str | dict[str, str | None]]:
35-
from hatch_fancy_pypi_readme._builder import build_text
36-
from hatch_fancy_pypi_readme._config import load_and_validate_config
37-
38-
config = load_and_validate_config(
39-
pyproject_dict["tool"]["hatch"]["metadata"]["hooks"]["fancy-pypi-readme"]
40-
)
41-
42-
return {
43-
"readme": {
44-
"content-type": config.content_type,
45-
"text": build_text(config.fragments, config.substitutions),
46-
}
47-
}
17+
def _load(mod_name: str, pyproject_dict: dict[str, Any]) -> dict[str, Any]:
18+
return importlib.import_module(mod_name).dynamic_metadata(pyproject_dict) # type: ignore[no-any-return]
4819

4920

5021
def get_standard_metadata(
@@ -53,33 +24,23 @@ def get_standard_metadata(
5324
metadata = StandardMetadata.from_pyproject(pyproject_dict)
5425

5526
# handle any dynamic metadata
56-
# start by collecting all the scikit-build entrypoints
57-
eps: importlib.metadata.EntryPoints = importlib.metadata.entry_points(
58-
group="scikit_build.metadata"
59-
)
60-
cached_plugins = {}
61-
for field, ep_name in settings.metadata.items():
27+
for field in settings.metadata:
6228
if field not in metadata.dynamic:
6329
msg = f"{field} is not in project.dynamic"
6430
raise KeyError(msg)
65-
if ep_name not in cached_plugins:
66-
try:
67-
ep = eps[ep_name]
68-
except KeyError:
69-
warnings.warn(
70-
f"could not find requested entrypoint {ep_name} for field {field}"
71-
)
72-
continue
73-
else:
74-
cached_plugins[ep_name] = ep.load()(pyproject_dict)
75-
if field in cached_plugins[ep_name]:
76-
# would be better to update the metadata directly but this is
77-
# currently not supported by pyproject_metadata
78-
# metadata.__setattr__(field, ep.load()(pyproject_path)
79-
pyproject_dict["project"][field] = cached_plugins[ep_name][field]
31+
32+
plugins = set(settings.metadata.values())
33+
cached_plugins = {key: _load(key, pyproject_dict) for key in plugins}
34+
35+
for field, mod_name in settings.metadata.items():
36+
# would be better to update the metadata directly but this is
37+
# currently not supported by pyproject_metadata
38+
# metadata.__setattr__(field, ep.load()(pyproject_path)
39+
if field in cached_plugins[mod_name]:
40+
pyproject_dict["project"][field] = cached_plugins[mod_name][field]
8041
pyproject_dict["project"]["dynamic"].remove(field)
8142
else:
82-
msg = f"{field} is not provided by plugin {ep_name}"
43+
msg = f"{field} is not provided by plugin {mod_name}"
8344
raise KeyError(msg)
8445

8546
# if pyproject-metadata supports updates, we won't need this line anymore

tests/packages/dynamic_metadata/faulty_project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ name = "fancy"
77
version = "0.0.1"
88

99
[tool.scikit-build.metadata]
10-
readme = "fancy_pypi_readme"
10+
readme = "scikit_build_core.metadata.fancy_pypi_readme"

tests/packages/dynamic_metadata/plugin_project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ name = "fancy"
77
dynamic = ["readme", "version"]
88

99
[tool.scikit-build.metadata]
10-
version = "setuptools_scm"
11-
readme = "fancy_pypi_readme"
10+
version = "scikit_build_core.metadata.setuptools_scm"
11+
readme = "scikit_build_core.metadata.fancy_pypi_readme"
1212

1313
[tool.setuptools_scm]
1414

tests/test_dynamic_metadata.py

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
from __future__ import annotations
22

3+
import importlib
34
import shutil
45
import sys
6+
import types
57
import zipfile
68
from pathlib import Path
79
from typing import Any
8-
from unittest import mock
910

1011
import git
1112
import pyproject_metadata
1213
import pytest
1314

14-
from scikit_build_core._compat import importlib, tomllib
15+
from scikit_build_core._compat import tomllib
1516
from scikit_build_core.build import build_wheel
1617
from scikit_build_core.settings.metadata import get_standard_metadata
1718
from scikit_build_core.settings.skbuild_read_settings import SettingsReader
@@ -53,30 +54,33 @@ def ep_dual(_pyproject_dict: dict[str, Any]) -> dict[str, str | dict[str, str |
5354
}
5455

5556

56-
@pytest.fixture()
57-
def mock_entry_points():
58-
mocked = importlib.metadata.EntryPoints(
59-
(
60-
importlib.metadata.EntryPoint( # type: ignore[no-untyped-call]
61-
"test_version", f"{__name__}:ep_version", "scikit_build.metadata"
62-
),
63-
importlib.metadata.EntryPoint( # type: ignore[no-untyped-call]
64-
"test_license", f"{__name__}:ep_license", "scikit_build.metadata"
65-
),
66-
importlib.metadata.EntryPoint( # type: ignore[no-untyped-call]
67-
"test_readme", f"{__name__}:ep_readme", "scikit_build.metadata"
68-
),
69-
importlib.metadata.EntryPoint( # type: ignore[no-untyped-call]
70-
"test_dual", f"{__name__}:ep_dual", "scikit_build.metadata"
71-
),
72-
)
73-
)
57+
original_loader = importlib.import_module
58+
59+
60+
def special_loader(name: str, *args: Any, **kwargs: Any) -> Any:
61+
if name == "test_version":
62+
test_version = types.ModuleType("test_version")
63+
test_version.dynamic_metadata = ep_version # type: ignore[attr-defined]
64+
return test_version
65+
if name == "test_readme":
66+
test_readme = types.ModuleType("test_readme")
67+
test_readme.dynamic_metadata = ep_readme # type: ignore[attr-defined]
68+
return test_readme
69+
if name == "test_license":
70+
test_license = types.ModuleType("test_license")
71+
test_license.dynamic_metadata = ep_license # type: ignore[attr-defined]
72+
return test_license
73+
if name == "test_dual":
74+
test_dual = types.ModuleType("test_dual")
75+
test_dual.dynamic_metadata = ep_dual # type: ignore[attr-defined]
76+
return test_dual
7477

75-
with mock.patch(
76-
"scikit_build_core.settings.metadata.importlib.metadata.entry_points"
77-
) as mocked_eps:
78-
mocked_eps.return_value = mocked
79-
yield mocked
78+
return original_loader(name, *args, **kwargs)
79+
80+
81+
@pytest.fixture()
82+
def mock_entry_points(monkeypatch):
83+
monkeypatch.setattr(importlib, "import_module", special_loader)
8084

8185

8286
@pytest.mark.usefixtures("mock_entry_points")
@@ -155,7 +159,7 @@ def test_warn_metadata(monkeypatch):
155159

156160
settings_reader.validate_may_exit()
157161

158-
with pytest.warns():
162+
with pytest.raises(ModuleNotFoundError):
159163
get_standard_metadata(pyproject, settings)
160164

161165

0 commit comments

Comments
 (0)