Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve pyproject.toml validation messages #3487

Merged
merged 2 commits into from
Aug 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.d/3487.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Modified ``pyproject.toml`` validation exception handling to
make relevant debugging information easier to spot.
12 changes: 8 additions & 4 deletions setuptools/config/pyprojecttoml.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,14 @@ def validate(config: dict, filepath: _Path) -> bool:
try:
return validator.validate(config)
except validator.ValidationError as ex:
_logger.error(f"configuration error: {ex.summary}") # type: ignore
_logger.debug(ex.details) # type: ignore
error = ValueError(f"invalid pyproject.toml config: {ex.name}") # type: ignore
raise error from None
summary = f"configuration error: {ex.summary}"
if ex.name.strip("`") != "project":
# Probably it is just a field missing/misnamed, not worthy the verbosity...
_logger.debug(summary)
_logger.debug(ex.details)

error = f"invalid pyproject.toml config: {ex.name}."
raise ValueError(f"{error}\n{summary}") from None


def apply_configuration(
Expand Down
23 changes: 5 additions & 18 deletions setuptools/tests/config/test_pyprojecttoml.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import logging
import re
from configparser import ConfigParser
from inspect import cleandoc
Expand Down Expand Up @@ -307,7 +306,7 @@ def test_ignore_unrelated_config(tmp_path, example):


@pytest.mark.parametrize(
"example, error_msg, value_shown_in_debug",
"example, error_msg",
[
(
"""
Expand All @@ -316,30 +315,18 @@ def test_ignore_unrelated_config(tmp_path, example):
version = "1.2"
requires = ['pywin32; platform_system=="Windows"' ]
""",
"configuration error: `project` must not contain {'requires'} properties",
'"requires": ["pywin32; platform_system==\\"Windows\\""]',
"configuration error: .project. must not contain ..requires.. properties",
),
],
)
def test_invalid_example(tmp_path, caplog, example, error_msg, value_shown_in_debug):
caplog.set_level(logging.DEBUG)
def test_invalid_example(tmp_path, example, error_msg):
pyproject = tmp_path / "pyproject.toml"
pyproject.write_text(cleandoc(example))

caplog.clear()
with pytest.raises(ValueError, match="invalid pyproject.toml"):
pattern = re.compile(f"invalid pyproject.toml.*{error_msg}.*", re.M | re.S)
with pytest.raises(ValueError, match=pattern):
read_configuration(pyproject)

# Make sure the logs give guidance to the user
error_log = caplog.record_tuples[0]
assert error_log[1] == logging.ERROR
assert error_msg in error_log[2]

debug_log = caplog.record_tuples[1]
assert debug_log[1] == logging.DEBUG
debug_msg = "".join(line.strip() for line in debug_log[2].splitlines())
assert value_shown_in_debug in debug_msg


@pytest.mark.parametrize("config", ("", "[tool.something]\nvalue = 42"))
def test_empty(tmp_path, config):
Expand Down