diff --git a/.bumpversion.cfg b/.bumpversion.cfg index e04e382..55f4b1b 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -4,7 +4,6 @@ commit = True tag = True [bumpversion:file:pyproject.toml] - search = version = "{current_version}" replace = version = "{new_version}" diff --git a/sphinx_pyproject/__init__.py b/sphinx_pyproject/__init__.py index 33cb827..62361ad 100644 --- a/sphinx_pyproject/__init__.py +++ b/sphinx_pyproject/__init__.py @@ -27,12 +27,13 @@ # # stdlib +import collections.abc from typing import Any, Dict, Iterator, List, Mapping, MutableMapping, Optional # 3rd party import dom_toml from dom_toml.decoder import TomlPureDecoder -from dom_toml.parser import TOML_TYPES, AbstractConfigParser, BadConfigError +from dom_toml.parser import TOML_TYPES, AbstractConfigParser, BadConfigError, construct_path from domdf_python_tools.paths import PathPlus from domdf_python_tools.typing import PathLike from domdf_python_tools.words import word_join @@ -132,18 +133,26 @@ def __init__( pep621_config = ProjectParser().parse(config["project"]) - for key in ("name", "version", "description"): - if key not in pep621_config: + dynamic = pep621_config.get("dynamic", []) + if "name" not in pep621_config: + raise BadConfigError( + f"Either 'name' was not declared in the 'project' table " + f"or it was marked as 'dynamic', which is unsupported by 'sphinx-pyproject'." + ) + + for key in ("version", "description"): + if key not in pep621_config and key not in dynamic: raise BadConfigError( - f"Either {key!r} was not declared in the 'project' table " - f"or it was marked as 'dynamic', which is unsupported by 'sphinx-pyproject'." + f"{key!r} was not declared in the 'project' table " + f"and was not marked as 'dynamic', which is unsupported by 'sphinx-pyproject'." ) if "author" not in pep621_config: - raise BadConfigError( - f"Either 'authors/maintainers' was not declared in the 'project' table " - f"or it was marked as 'dynamic', which is unsupported by 'sphinx-pyproject'." - ) + if "author" not in dynamic and "maintainer" not in dynamic: + raise BadConfigError( + f"'authors/maintainers' was not declared in the 'project' table " + f"and was not marked as 'dynamic', which is unsupported by 'sphinx-pyproject'." + ) self.name = pep621_config["name"] self.version = pep621_config["version"] @@ -224,6 +233,25 @@ def parse_description(self, config: Dict[str, TOML_TYPES]) -> str: self.assert_type(description, str, ["project", "description"]) return description + def parse_dynamic(self, config: Dict[str, TOML_TYPES]) -> List[str]: + """ + Parse the :pep621:`dynamic` key. + + :param config: The unparsed TOML config for the ``[project]`` table. + """ + + dynamic = config["dynamic"] + + if isinstance(dynamic, str): + name = construct_path(["project", "dynamic"]) + raise TypeError( + f"Invalid type for {name!r}: " + f"expected , got {type(dynamic)!r}", + ) + + self.assert_type(dynamic, collections.abc.Sequence, ["project", "dynamic"]) + return dynamic + @staticmethod def parse_author(config: Dict[str, TOML_TYPES]) -> str: """ @@ -259,6 +287,7 @@ def keys(self) -> List[str]: "name", "version", "description", + "dynamic", "author", ] diff --git a/tests/test_sphinx_pyproject.py b/tests/test_sphinx_pyproject.py index 1af1244..50b79f2 100644 --- a/tests/test_sphinx_pyproject.py +++ b/tests/test_sphinx_pyproject.py @@ -132,10 +132,6 @@ def test_authors_commas(tmp_pathplus: PathPlus): @pytest.mark.parametrize( "config", [ - pytest.param("[project]", id="empty_project"), - pytest.param("[project]\n", id="name_only"), - pytest.param("[project]\nversion = '1.2.3'", id="version_only"), - pytest.param("[project]\ndescription = 'Description'", id="description_only"), pytest.param("[project]\nname = 'foo'\ndescription = 'Description'", id="name_description"), pytest.param("[project]\nname = 'foo'\nversion = '1.2.3'", id="name_description"), pytest.param( @@ -146,6 +142,27 @@ def test_authors_commas(tmp_pathplus: PathPlus): def test_missing_keys(tmp_pathplus: PathPlus, config: str): (tmp_pathplus / "pyproject.toml").write_text(config) + err = ( + "'.*' was not declared in the 'project' table " + "and was not marked as 'dynamic', which is unsupported by 'sphinx-pyproject'." + ) + + with pytest.raises(BadConfigError, match=err): + SphinxConfig(tmp_pathplus / "pyproject.toml") + + +@pytest.mark.parametrize( + "config", + [ + pytest.param("[project]", id="empty_project"), + pytest.param("[project]\n", id="name_only"), + pytest.param("[project]\nversion = '1.2.3'", id="version_only"), + pytest.param("[project]\ndescription = 'Description'", id="description_only"), + ] + ) +def test_missing_name(tmp_pathplus: PathPlus, config: str): + (tmp_pathplus / "pyproject.toml").write_text(config) + err = ( "Either '.*' was not declared in the 'project' table " "or it was marked as 'dynamic', which is unsupported by 'sphinx-pyproject'." diff --git a/tests/test_sphinx_pyproject_/test_parse_our_config.yml b/tests/test_sphinx_pyproject_/test_parse_our_config.yml index 4fae884..7846720 100644 --- a/tests/test_sphinx_pyproject_/test_parse_our_config.yml +++ b/tests/test_sphinx_pyproject_/test_parse_our_config.yml @@ -20,6 +20,10 @@ autodoc_exclude_members: - __hash__ copyright: 2021-2023 Dominic Davis-Foster description: Move some of your Sphinx configuration into pyproject.toml +dynamic: +- requires-python +- classifiers +- dependencies extensions: - sphinx_toolbox - sphinx_toolbox.more_autodoc