diff --git a/src/incremental/__init__.py b/src/incremental/__init__.py index b10d5be..7a29fd7 100644 --- a/src/incremental/__init__.py +++ b/src/incremental/__init__.py @@ -482,11 +482,20 @@ def _load_pyproject_toml(toml_path): # type: (str) -> Optional[_IncrementalConf tool_incremental = _extract_tool_incremental(data) - if tool_incremental is None or tool_incremental == {}: + package = None + if tool_incremental is not None and "name" in tool_incremental: + package = tool_incremental["name"] + if package is None: try: package = data["project"]["name"] except KeyError: - raise ValueError("""\ + pass + if package is None: + # We can't proceed without a project name, but that's only an error + # if [tool.incremental] is present. + if tool_incremental is None: + return None + raise ValueError("""\ Couldn't extract the package name from pyproject.toml. Specify it like: [project] @@ -496,12 +505,8 @@ def _load_pyproject_toml(toml_path): # type: (str) -> Optional[_IncrementalConf [tool.incremental] name = "Foo" -""") - elif tool_incremental.keys() == {"name"}: - package = tool_incremental["name"] - else: - raise ValueError("Unexpected key(s) in [tool.incremental]") +""") if not isinstance(package, str): raise TypeError( "Package name must be a string, but found {}".format(type(package)) @@ -526,6 +531,8 @@ def _extract_tool_incremental(data): # type: (Dict[str, object]) -> Optional[Di if not isinstance(tool_incremental, dict): raise ValueError("[tool.incremental] must be a table") + if not {"name"}.issuperset(tool_incremental.keys()): + raise ValueError("Unexpected key(s) in [tool.incremental]") return tool_incremental diff --git a/src/incremental/newsfragments/100.bugfix b/src/incremental/newsfragments/100.bugfix new file mode 100644 index 0000000..289e36f --- /dev/null +++ b/src/incremental/newsfragments/100.bugfix @@ -0,0 +1 @@ +Incremental 24.7.0 would produce an error when parsing the ``pyproject.toml`` of a project that lacked the ``use_incremental=True`` or ``[tool.incremental]`` opt-in markers if that file lacked a ``[project]`` section containing the package name. This could cause a project that only uses ``pyproject.toml`` to configure tools to fail to build if Incremental is installed. Incremental now ignores such projects. diff --git a/src/incremental/tests/test_pyproject.py b/src/incremental/tests/test_pyproject.py index df5b0ca..036f5a0 100644 --- a/src/incremental/tests/test_pyproject.py +++ b/src/incremental/tests/test_pyproject.py @@ -49,14 +49,24 @@ def test_fileNotFound(self): path = os.path.join(cast(str, self.mktemp()), "pyproject.toml") self.assertIsNone(_load_pyproject_toml(path)) - def test_nameMissing(self): + def test_configMissing(self): """ - `ValueError` is raised when ``[tool.incremental]`` is present but - he project name isn't available. + A ``pyproject.toml`` that exists but provides no relevant configuration + is ignored. """ for toml in [ "\n", "[tool.notincremental]\n", + "[project]\n", + ]: + self.assertIsNone(self._loadToml(toml)) + + def test_nameMissing(self): + """ + `ValueError` is raised when ``[tool.incremental]`` is present but + the project name isn't available. + """ + for toml in [ "[tool.incremental]\n", "[project]\n[tool.incremental]\n", ]: diff --git a/tests/example_noop/example_noop/__init__.py b/tests/example_noop/example_noop/__init__.py new file mode 100644 index 0000000..0ae478d --- /dev/null +++ b/tests/example_noop/example_noop/__init__.py @@ -0,0 +1,11 @@ +""" +An example setuptools project that doesn't use Incremental at all. + +This is used to verify that Incremental correctly deactivates itself +when there is no opt-in, even though setuptools always invokes its +hook. +""" + +__version__ = "100" + +__all__ = ["__version__"] diff --git a/tests/example_noop/pyproject.toml b/tests/example_noop/pyproject.toml new file mode 100644 index 0000000..39a8911 --- /dev/null +++ b/tests/example_noop/pyproject.toml @@ -0,0 +1,3 @@ +# This file exists, but doesn't contain any [project] or +# [tool.incremental] configuration so Incremental ignores it. +[tool.something] diff --git a/tests/example_noop/setup.py b/tests/example_noop/setup.py new file mode 100644 index 0000000..e4ce3e3 --- /dev/null +++ b/tests/example_noop/setup.py @@ -0,0 +1,13 @@ +from setuptools import setup + +setup( + name="example_noop", + version="100", + packages=["example_noop"], + zip_safe=False, + setup_requires=[ + # Ensure Incremental is installed for test purposes + # (but it won't do anything). + "incremental", + ], +) diff --git a/tests/test_examples.py b/tests/test_examples.py index 5d83e3c..f9ad2c8 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -97,3 +97,15 @@ def test_hatch_version_set(self): # Hatch may wrap the output, so we are flexible about the specifics of whitespace. suggestion.replace(b".", rb"\.").replace(b" ", b"\\s+"), ) + + def test_noop(self): + """ + The Incremental setuptools hook is a silent no-op when there is no Incremental + configuration to be found. + """ + build_and_install(TEST_DIR.child("example_noop")) + + import example_noop + + self.assertEqual(example_noop.__version__, "100") + self.assertEqual(metadata.version("example_noop"), "100")