diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py index 40b964585..a326df482 100644 --- a/mesonpy/__init__.py +++ b/mesonpy/__init__.py @@ -41,6 +41,15 @@ else: import tomllib + +if sys.version_info < (3, 8): + import importlib_metadata +else: + import importlib.metadata as importlib_metadata + + +import packaging.requirements +import packaging.version import pyproject_metadata import mesonpy._compat @@ -658,10 +667,32 @@ def build_editable(self, directory: Path, verbose: bool = False) -> pathlib.Path return wheel_file +class MesonpyVersion(packaging.version.Version): + def __getitem__(self, key): + return self.release[key] + + +class _parse_version(version: str) -> Union[packaging.version.LegacyVersion, MesonpyVersion]: + try: + return MesonpyVersion(version) + except packaging.version.InvalidVersion: + return packaging.version.LegacyVersion(version) + + +def _compute_dynamic_dependencies(templates) -> List[str]: + dependencies = [] + for template in templates: + req = packaging.requirements.Requirement(template) + version = _parse_version(importlib_metadata.version(req.name)) + dependencies.append(template.format(v=version)) + return dependencies + + class Project(): """Meson project wrapper to generate Python artifacts.""" _ALLOWED_DYNAMIC_FIELDS: ClassVar[List[str]] = [ + 'dependencies', 'version', ] _metadata: Optional[pyproject_metadata.StandardMetadata] @@ -778,6 +809,10 @@ def __init__( # noqa: C901 if self._metadata and 'version' in self._metadata.dynamic: self._metadata.version = self.version + if self._metadata and 'dependencies' in self._metadata.dynamic: + self._metadata.dependencies = _compute_dynamic_dependencies( + self._get_config_key('dynamic').get('dependencies', [])) + def _get_config_key(self, key: str) -> Any: value: Any = self._config for part in f'tool.meson-python.{key}'.split('.'): diff --git a/pyproject.toml b/pyproject.toml index e7f583a06..40bfbaa45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,7 @@ build-backend = 'mesonpy' backend-path = ['.'] requires = [ 'meson>=0.63.3', + 'packaging', 'pyproject-metadata>=0.7.1', 'tomli>=1.0.0; python_version<"3.11"', ] @@ -29,6 +30,7 @@ classifiers = [ dependencies = [ 'colorama; os_name == "nt"', 'meson>=0.63.3', + 'packaging', 'pyproject-metadata>=0.7.1', 'tomli>=1.0.0; python_version<"3.11"', ] diff --git a/tests/packages/dynamic-dependencies/meson.build b/tests/packages/dynamic-dependencies/meson.build new file mode 100644 index 000000000..9f136bb9e --- /dev/null +++ b/tests/packages/dynamic-dependencies/meson.build @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 The meson-python developers +# +# SPDX-License-Identifier: MIT + +project('dynamic-dependencies', version: '1.0.0') diff --git a/tests/packages/dynamic-dependencies/pyproject.toml b/tests/packages/dynamic-dependencies/pyproject.toml new file mode 100644 index 000000000..f5789b0b4 --- /dev/null +++ b/tests/packages/dynamic-dependencies/pyproject.toml @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2023 The meson-python developers +# +# SPDX-License-Identifier: MIT + +[build-system] +build-backend = 'mesonpy' +requires = ['meson-python'] + +[project] +name = 'dynamic-dependencies' +version = '1.0.0' +dynamic = [ + 'dependencies', +] + +[tool.meson-python.dynamic] +dependencies = [ + 'meson ~= {v.major}.{v.minor}', + 'meson-python >= {v}', +] diff --git a/tests/packages/unsupported-dynamic/pyproject.toml b/tests/packages/unsupported-dynamic/pyproject.toml index ee63c29b9..2886363e7 100644 --- a/tests/packages/unsupported-dynamic/pyproject.toml +++ b/tests/packages/unsupported-dynamic/pyproject.toml @@ -10,5 +10,5 @@ requires = ['meson-python'] name = 'unsupported-dynamic' version = '1.0.0' dynamic = [ - 'dependencies', + 'requires-python', ] diff --git a/tests/test_project.py b/tests/test_project.py index 726238131..241717a61 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -39,7 +39,7 @@ def test_version(package): def test_unsupported_dynamic(package_unsupported_dynamic): - with pytest.raises(mesonpy.MesonBuilderError, match='Unsupported dynamic fields: "dependencies"'): + with pytest.raises(mesonpy.MesonBuilderError, match='Unsupported dynamic fields: "requires-python"'): with mesonpy.Project.with_temp_working_dir(): pass