Skip to content

Commit

Permalink
Add warning for deprecated project.license as TOML table (#4840 )
Browse files Browse the repository at this point in the history
  • Loading branch information
abravalheri authored Feb 18, 2025
2 parents 6f0aee2 + 68397dc commit 10c3e7c
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 31 deletions.
5 changes: 5 additions & 0 deletions newsfragments/4840.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Deprecated ``project.license`` as a TOML table in
``pyproject.toml``. Users are expected to move towards using
``project.license-files`` and/or SPDX expressions (as strings) in
``pyproject.license``.
See :pep:`PEP 639 <639#deprecate-license-key-table-subkeys>`.
8 changes: 8 additions & 0 deletions setuptools/config/_apply_pyprojecttoml.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,14 @@ def _license(dist: Distribution, val: str | dict, root_dir: StrPath | None):
if isinstance(val, str):
_set_config(dist, "license_expression", _static.Str(val))
else:
pypa_guides = "guides/writing-pyproject-toml/#license"
SetuptoolsDeprecationWarning.emit(
"`project.license` as a TOML table is deprecated",
"Please use a simple string containing a SPDX expression for "
"`project.license`. You can also use `project.license-files`.",
see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
due_date=(2026, 2, 18), # Introduced on 2025-02-18
)
if "file" in val:
# XXX: Is it completely safe to assume static?
value = expand.read_files([val["file"]], root_dir)
Expand Down
86 changes: 58 additions & 28 deletions setuptools/tests/config/test_apply_pyprojecttoml.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def test_apply_pyproject_equivalent_to_setupcfg(url, monkeypatch, tmp_path):
description = "Lovely Spam! Wonderful Spam!"
readme = "README.rst"
requires-python = ">=3.8"
license = {file = "LICENSE.txt"}
license-files = ["LICENSE.txt"] # Updated to be PEP 639 compliant
keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"]
authors = [
{email = "[email protected]"},
Expand Down Expand Up @@ -206,7 +206,6 @@ def test_pep621_example(tmp_path):
"""Make sure the example in PEP 621 works"""
pyproject = _pep621_example_project(tmp_path)
dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
assert dist.metadata.license == "--- LICENSE stub ---"
assert set(dist.metadata.license_files) == {"LICENSE.txt"}


Expand Down Expand Up @@ -294,6 +293,11 @@ def test_utf8_maintainer_in_metadata( # issue-3663
'License: MIT',
'License-Expression: ',
id='license-text',
marks=[
pytest.mark.filterwarnings(
"ignore:.project.license. as a TOML table is deprecated",
)
],
),
pytest.param(
PEP639_LICENSE_EXPRESSION,
Expand Down Expand Up @@ -354,47 +358,51 @@ def test_license_classifier_without_license_expression(tmp_path):
"""
pyproject = _pep621_example_project(tmp_path, "README", text)

msg = "License classifiers are deprecated(?:.|\n)*MIT License"
with pytest.warns(SetuptoolsDeprecationWarning, match=msg):
msg1 = "License classifiers are deprecated(?:.|\n)*MIT License"
msg2 = ".project.license. as a TOML table is deprecated"
with (
pytest.warns(SetuptoolsDeprecationWarning, match=msg1),
pytest.warns(SetuptoolsDeprecationWarning, match=msg2),
):
dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)

# Check license classifier is still included
assert dist.metadata.get_classifiers() == ["License :: OSI Approved :: MIT License"]


class TestLicenseFiles:
def base_pyproject(self, tmp_path, additional_text):
pyproject = _pep621_example_project(tmp_path, "README")
text = pyproject.read_text(encoding="utf-8")

# Sanity-check
assert 'license = {file = "LICENSE.txt"}' in text
assert "[tool.setuptools]" not in text

text = f"{text}\n{additional_text}\n"
pyproject.write_text(text, encoding="utf-8")
return pyproject

def base_pyproject_license_pep639(self, tmp_path, additional_text=""):
pyproject = _pep621_example_project(tmp_path, "README")
text = pyproject.read_text(encoding="utf-8")
def base_pyproject(
self,
tmp_path,
additional_text="",
license_toml='license = {file = "LICENSE.txt"}\n',
):
text = PEP639_LICENSE_EXPRESSION

# Sanity-check
assert 'license = {file = "LICENSE.txt"}' in text
assert 'license = "mit or apache-2.0"' in text
assert 'license-files' not in text
assert "[tool.setuptools]" not in text

text = re.sub(
r"(license = {file = \"LICENSE.txt\"})\n",
("license = \"licenseref-Proprietary\"\nlicense-files = [\"_FILE*\"]\n"),
r"(license = .*)\n",
license_toml,
text,
count=1,
)
if additional_text:
text = f"{text}\n{additional_text}\n"
pyproject.write_text(text, encoding="utf-8")
assert license_toml in text # sanity check
text = f"{text}\n{additional_text}\n"
pyproject = _pep621_example_project(tmp_path, "README", pyproject_text=text)
return pyproject

def base_pyproject_license_pep639(self, tmp_path, additional_text=""):
return self.base_pyproject(
tmp_path,
additional_text=additional_text,
license_toml='license = "licenseref-Proprietary"'
'\nlicense-files = ["_FILE*"]\n',
)

def test_both_license_and_license_files_defined(self, tmp_path):
setuptools_config = '[tool.setuptools]\nlicense-files = ["_FILE*"]'
pyproject = self.base_pyproject(tmp_path, setuptools_config)
Expand All @@ -407,8 +415,12 @@ def test_both_license_and_license_files_defined(self, tmp_path):
license = tmp_path / "LICENSE.txt"
license.write_text("LicenseRef-Proprietary\n", encoding="utf-8")

msg = "'tool.setuptools.license-files' is deprecated in favor of 'project.license-files'"
with pytest.warns(SetuptoolsDeprecationWarning, match=msg):
msg1 = "'tool.setuptools.license-files' is deprecated in favor of 'project.license-files'"
msg2 = ".project.license. as a TOML table is deprecated"
with (
pytest.warns(SetuptoolsDeprecationWarning, match=msg1),
pytest.warns(SetuptoolsDeprecationWarning, match=msg2),
):
dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
assert set(dist.metadata.license_files) == {"_FILE.rst", "_FILE.txt"}
assert dist.metadata.license == "LicenseRef-Proprietary\n"
Expand Down Expand Up @@ -437,17 +449,35 @@ def test_license_files_defined_twice(self, tmp_path):
def test_default_patterns(self, tmp_path):
setuptools_config = '[tool.setuptools]\nzip-safe = false'
# ^ used just to trigger section validation
pyproject = self.base_pyproject(tmp_path, setuptools_config)
pyproject = self.base_pyproject(tmp_path, setuptools_config, license_toml="")

license_files = "LICENCE-a.html COPYING-abc.txt AUTHORS-xyz NOTICE,def".split()

for fname in license_files:
(tmp_path / fname).write_text(f"{fname}\n", encoding="utf-8")

dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)

assert (tmp_path / "LICENSE.txt").exists() # from base example
assert set(dist.metadata.license_files) == {*license_files, "LICENSE.txt"}

def test_deprecated_file_expands_to_text(self, tmp_path):
"""Make sure the old example with ``license = {text = ...}`` works"""

assert 'license-files = ["LICENSE.txt"]' in PEP621_EXAMPLE # sanity check
text = PEP621_EXAMPLE.replace(
'license-files = ["LICENSE.txt"]',
'license = {file = "LICENSE.txt"}',
)
pyproject = _pep621_example_project(tmp_path, pyproject_text=text)

msg = ".project.license. as a TOML table is deprecated"
with pytest.warns(SetuptoolsDeprecationWarning, match=msg):
dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)

assert dist.metadata.license == "--- LICENSE stub ---"
assert set(dist.metadata.license_files) == {"LICENSE.txt"} # auto-filled


class TestPyModules:
# https://github.com/pypa/setuptools/issues/4316
Expand Down
8 changes: 6 additions & 2 deletions setuptools/tests/test_build_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,9 +387,13 @@ def test_build_with_pyproject_config(self, tmpdir, setup_script):
build_backend = self.get_build_backend()
with tmpdir.as_cwd():
path.build(files)
msgs = [
"'tool.setuptools.license-files' is deprecated in favor of 'project.license-files'",
"`project.license` as a TOML table is deprecated",
]
with warnings.catch_warnings():
msg = "'tool.setuptools.license-files' is deprecated in favor of 'project.license-files'"
warnings.filterwarnings("ignore", msg, SetuptoolsDeprecationWarning)
for msg in msgs:
warnings.filterwarnings("ignore", msg, SetuptoolsDeprecationWarning)
sdist_path = build_backend.build_sdist("temp")
wheel_file = build_backend.build_wheel("temp")

Expand Down
14 changes: 13 additions & 1 deletion setuptools/tests/test_sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -708,12 +708,21 @@ def test_sdist_with_latin1_encoded_filename(self):
[project]
name = "testing"
readme = "USAGE.rst"
license = {file = "DOWHATYOUWANT"}
license-files = ["DOWHATYOUWANT"]
dynamic = ["version"]
[tool.setuptools.dynamic]
version = {file = ["src/VERSION.txt"]}
""",
"pyproject.toml - directive with str instead of list": """
[project]
name = "testing"
readme = "USAGE.rst"
license-files = ["DOWHATYOUWANT"]
dynamic = ["version"]
[tool.setuptools.dynamic]
version = {file = "src/VERSION.txt"}
""",
"pyproject.toml - deprecated license table with file entry": """
[project]
name = "testing"
readme = "USAGE.rst"
Expand All @@ -725,6 +734,9 @@ def test_sdist_with_latin1_encoded_filename(self):
}

@pytest.mark.parametrize("config", _EXAMPLE_DIRECTIVES.keys())
@pytest.mark.filterwarnings(
"ignore:.project.license. as a TOML table is deprecated"
)
def test_add_files_referenced_by_config_directives(self, source_dir, config):
config_file, _, _ = config.partition(" - ")
config_text = self._EXAMPLE_DIRECTIVES[config]
Expand Down

0 comments on commit 10c3e7c

Please sign in to comment.