Skip to content

Commit

Permalink
Add initial support for License-Expression (PEP 639)
Browse files Browse the repository at this point in the history
  • Loading branch information
cdce8p committed Jan 9, 2025
1 parent d6fd6eb commit 87163bc
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 12 deletions.
2 changes: 1 addition & 1 deletion docs/userguide/pyproject_config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ The ``project`` table contains metadata fields as described by the
readme = "README.rst"
requires-python = ">=3.8"
keywords = ["one", "two"]
license = {text = "BSD-3-Clause"}
license = "BSD-3-Clause"
classifiers = [
"Framework :: Django",
"Programming Language :: Python :: 3",
Expand Down
1 change: 1 addition & 0 deletions newsfragments/4706.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added initial support for ``License-Expression`` (`PEP 639 <https://peps.python.org/pep-0639/#add-license-expression-field>`_). -- by :user:`cdce8p`
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ authors = [
]
description = "Easily download, build, install, upgrade, and uninstall Python packages"
readme = "README.rst"
license = "MIT"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Software Development :: Libraries :: Python Modules",
Expand Down
10 changes: 7 additions & 3 deletions setuptools/_core_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def read_pkg_file(self, file):
self.url = _read_field_from_msg(msg, 'home-page')
self.download_url = _read_field_from_msg(msg, 'download-url')
self.license = _read_field_unescaped_from_msg(msg, 'license')
self.license_expression = _read_field_unescaped_from_msg(msg, 'license_expression')

self.long_description = _read_field_unescaped_from_msg(msg, 'description')
if self.long_description is None and self.metadata_version >= Version('2.1'):
Expand Down Expand Up @@ -175,9 +176,12 @@ def write_field(key, value):
if attr_val is not None:
write_field(field, attr_val)

license = self.get_license()
if license:
write_field('License', rfc822_escape(license))
if self.license_expression:
write_field('License-Expression', rfc822_escape(self.license_expression))
else:
license = self.get_license()
if license:
write_field('License', rfc822_escape(license))

for label, url in self.project_urls.items():
write_field('Project-URL', f'{label}, {url}')
Expand Down
17 changes: 10 additions & 7 deletions setuptools/config/_apply_pyprojecttoml.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,16 +181,19 @@ def _long_description(
dist._referenced_files.add(file)


def _license(dist: Distribution, val: dict, root_dir: StrPath | None):
def _license(dist: Distribution, val: str | dict, root_dir: StrPath | None):
from setuptools.config import expand

if "file" in val:
# XXX: Is it completely safe to assume static?
value = expand.read_files([val["file"]], root_dir)
_set_config(dist, "license", _static.Str(value))
dist._referenced_files.add(val["file"])
if isinstance(val, str):
_set_config(dist, "license_expression", _static.Str(val))
else:
_set_config(dist, "license", _static.Str(val["text"]))
if "file" in val:
# XXX: Is it completely safe to assume static?
value = expand.read_files([val["file"]], root_dir)
_set_config(dist, "license", _static.Str(value))
dist._referenced_files.add(val["file"])
else:
_set_config(dist, "license", _static.Str(val["text"]))


def _people(dist: Distribution, val: list[dict], _root_dir: StrPath | None, kind: str):
Expand Down
1 change: 1 addition & 0 deletions setuptools/dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ class Distribution(_Distribution):
'long_description_content_type': lambda: None,
'project_urls': dict,
'provides_extras': dict, # behaves like an ordered set
'license_expression': lambda: None,
'license_file': lambda: None,
'license_files': lambda: None,
'install_requires': list,
Expand Down
63 changes: 63 additions & 0 deletions setuptools/tests/config/test_apply_pyprojecttoml.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,28 @@ def main_gui(): pass
def main_tomatoes(): pass
"""

PEP639_LICENSE_TEXT = """\
[project]
name = "spam"
version = "2020.0.0"
authors = [
{email = "hi@pradyunsg.me"},
{name = "Tzu-Ping Chung"}
]
license = {text = "MIT"}
"""

PEP639_LICENSE_EXPRESSION = """\
[project]
name = "spam"
version = "2020.0.0"
authors = [
{email = "hi@pradyunsg.me"},
{name = "Tzu-Ping Chung"}
]
license = "MIT"
"""


def _pep621_example_project(
tmp_path,
Expand Down Expand Up @@ -251,6 +273,47 @@ def test_utf8_maintainer_in_metadata( # issue-3663
assert f"Maintainer-email: {expected_maintainers_meta_value}" in content


@pytest.mark.parametrize(
('pyproject_text', 'license', 'license_expression', 'content_str'),
(
pytest.param(
PEP639_LICENSE_TEXT,
'MIT',
None,
'License: MIT',
id='license-text',
),
pytest.param(
PEP639_LICENSE_EXPRESSION,
None,
'MIT',
'License-Expression: MIT',
id='license-expression',
),
),
)
def test_license_in_metadata(
license,
license_expression,
content_str,
pyproject_text,
tmp_path,
):
pyproject = _pep621_example_project(
tmp_path,
"README",
pyproject_text=pyproject_text,
)
dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
assert dist.metadata.license == license
assert dist.metadata.license_expression == license_expression
pkg_file = tmp_path / "PKG-FILE"
with open(pkg_file, "w", encoding="utf-8") as fh:
dist.metadata.write_pkg_file(fh)
content = pkg_file.read_text(encoding="utf-8")
assert content_str in content


class TestLicenseFiles:
# TODO: After PEP 639 is accepted, we have to move the license-files
# to the `project` table instead of `tool.setuptools`
Expand Down

0 comments on commit 87163bc

Please sign in to comment.