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

Prevent deprecated license classifiers from being written to core metadata #4833

Merged
merged 9 commits into from
Feb 18, 2025

Conversation

abravalheri
Copy link
Contributor

Follow up on the conversation in #4706 (comment)

Pull Request Checklist

Sorry, something went wrong.

@abravalheri
Copy link
Contributor Author

abravalheri commented Feb 17, 2025

@cdce8p does this make sense to you?

Considering the text of the PEP 639:

If the License-Expression field is present, build tools ... MUST NOT add such classifiers themselves.

I guess this depends on the interpretation of the PEP: setuptools is not adding the classifier by itself... The user is adding the classifier, setuptools is just letting it there.

due_date=(2027, 2, 17), # Introduced 2025-02-17
)
if license_classifiers:
# Filter classifiers but preserve "static-ness" of metadata
Copy link
Contributor Author

@abravalheri abravalheri Feb 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we should indeed preserve the "static-ness" of the classifiers, or simply let Dynamic: classifier appear in the core metadata (we are modifying them after all...)

@abravalheri abravalheri marked this pull request as ready for review February 17, 2025 16:47
Comment on lines 426 to 430
if license_classifiers:
# Filter classifiers but preserve "static-ness" of metadata
list_ = _static.List if _static.is_static(classifiers) else list
filtered = (cl for cl in classifiers if cl not in license_classifiers)
self.metadata.set_classifiers(list_(filtered))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think setuptools should remove the license classifier. Consider the build is done is CI, this could lead to cases were packages are distributed without any license information (neither license expression nor license classifier). This would even apply to setuptools itself.

Warn about it yes, but we shouldn't remove items from the classifier list.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review @cdce8p.

In this case it will not be distributed without license information right? (Because metadata.license_expression is set).

But I am rethinking... There is a chance either twine or PyPI themselves will fail the package validation if they decide to go for a more strict interpretation of the PEP. So we might implement your initial suggestion of error for users opting into license = "..." in pyproject.toml and warning elsewhere. Let me make another commit.

This would even apply to setuptools itself.

In the case of setuptools I believe that the best way of convying the license information is to use License-File instead of License-Expression given the complexity and maintenance of vendored dependencies (I saw your comment in the discourse thread, but the text in the SPDX standard seems to indicate that the correct would be to use an ... AND ... expression. This would require an evolution of the tooling for vendoring and increase complexity).

TL;DR: My impression is that if the licensing is easy enough, people should use the SPDX, but when it starts to get more complicated the best is to rely on the actual license files so that no mistake is introduced.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case it will not be distributed without license information right? (Because metadata.license_expression is set).

Yes. Missed the sneaky if license_expr I added myself 🙈

But I am rethinking... There is a chance either twine or PyPI themselves will fail the package validation if they decide to go for a more strict interpretation of the PEP. So we might implement your initial suggestion of error for users opting into license = "..." in pyproject.toml and warning elsewhere. Let me make another commit.

👍🏻

In the case of setuptools I believe that the best way of convying the license information is to use License-File instead of License-Expression given the complexity and maintenance of vendored dependencies (I saw your comment in the discourse thread, but the text in the SPDX standard seems to indicate that the correct would be to use an ... AND ... expression. This would require an evolution of the tooling for vendoring and increase complexity).

TL;DR: My impression is that if the licensing is easy enough, people should use the SPDX, but when it starts to get more complicated the best is to rely on the actual license files so that no mistake is introduced.

My understanding of the PEP might be wrong but the way I see it is that the license itself is determined by the license expression and the license files are only there to guarantee that the license information are properly distributed as well. Most licenses require the license text itself to be part of the distribution which often didn't happen in the past.

Copy link
Contributor Author

@abravalheri abravalheri Feb 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding of the PEP might be wrong but the way I see it is that the license itself is determined by the license expression and the license files are only there to guarantee that the license information are properly distributed as well.

If that is how the PEP intends it, then it is very problematic...

But I don't know if that is the case...

After all, the license files are the ultimate source of truth for the licensing model, that is the whole point. SPDX indexes are nice to have (if correctly derived), but ultimately is the text of the license that determines how the project can be distributed or not.

Some of the conversations of the discourse thread seem to hint at the same conclusion:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I am rethinking... There is a chance either twine or PyPI themselves will fail the package validation if they decide to go for a more strict interpretation of the PEP. So we might implement your initial suggestion of error for users opting into license = "..." in pyproject.toml and warning elsewhere. Let me make another commit.

@cdce8p, in ea4095d I updated the PR to warn or raise an exception accordingly.

@abravalheri abravalheri mentioned this pull request Feb 17, 2025
6 tasks
Copy link
Contributor

@cdce8p cdce8p left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some minor comments. IMO the current state does make sense, i.e. emit an error if a license expressions is specified and a warning if it isn't. While not removing an classifiers during the build.

Regarding the SPDX expression for setuptools itself. I'll be looking how numpy and pandas address the issue. Still think there are arguments for both interpretations but that's not something which needs to be discussed to completion right now. Keeping the existing MIT classifier should be fine at the moment.

Comment on lines 426 to 431
pypa_guides = "guides/licensing-examples-and-user-scenarios/"
SetuptoolsDeprecationWarning.emit(
"License classifiers are deprecated.",
"Please consider removing the following classifiers in favor of a "
"SPDX license expression:\n\n" + "\n".join(license_classifiers),
see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't the the licensing user guide to be compelling just yet. Maybe a better location would be the pyproject.toml guide? https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license

Comment on lines 330 to 332
with pytest.raises(InvalidConfigError, match=msg) as exc:
pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
assert "License :: OSI Approved :: MIT License" in str(exc.value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assert here will never execute. pytest.raises changing the warnings filter internally to be able to catch the InvalidConfigError. I.e. pyprojecttoml.apply_configuration raises and the assert is skipped.

You'll have to add a second call with the warnings filter set to ignore to be able to use assert.

Comment on lines 346 to 351
with pytest.warns(SetuptoolsDeprecationWarning, match=msg):
dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
# Check license classifier is still included
assert dist.metadata.get_classifiers() == [
"Development Status :: 5 - Production/Stable",
"Programming Language :: Python",
"License :: OSI Approved :: MIT License",
"License :: OSI Approved :: MIT License"
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue here.

Copy link
Contributor

@cdce8p cdce8p left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Comment on lines +347 to +351
with pytest.warns(SetuptoolsDeprecationWarning, match=msg):
dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
# Check license classifier is still included
assert dist.metadata.get_classifiers() == [
"Development Status :: 5 - Production/Stable",
"Programming Language :: Python",
"License :: OSI Approved :: MIT License",
]

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed that this uses pytest.warns instead of pytest.raises. So my previous post here was wrong. The nested block is actually fully executed.

Anyway, the current setup will also work.

@abravalheri abravalheri merged commit b80ee83 into pypa:feature/pep639 Feb 18, 2025
20 of 24 checks passed
@abravalheri abravalheri deleted the license-classifiers branch February 18, 2025 12:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants