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

[Docs] Need migration guide for license expression (PEP 639) about TOML-table-based project.license deprecation if v77 unavailable #4903

Open
1 task done
XuehaiPan opened this issue Mar 20, 2025 · 9 comments

Comments

@XuehaiPan
Copy link

XuehaiPan commented Mar 20, 2025

Summary

TL;DR: TOML-table-based project.license is deprecated in the newest setuptools. But the highest supported setuptools on Python 3.8 only supports TOML-table-based project.license.


setuptools v77.0.0 adds license expression (PEP 639) support in:

cc @abravalheri @cdce8p

For pyproject.toml with license = { text = "expression" }:

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "xxx"
description = "XXX"
readme = "README.md"
requires-python = ">= 3.8"
license = { text = "Apache-2.0" }

It will emit a warning during installation:

  /.../site-packages/setuptools/config/_apply_pyprojecttoml.py:82: SetuptoolsDeprecationWarning: `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`.

          By 2026-Feb-18, you need to update your project and remove deprecated calls
          or your builds will no longer be supported.

          See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license for details.
          ********************************************************************************

  !!

However, setuptools v77.0.0 does not support Python 3.8. The maximum supported version of Python 3.8 is v75.3.2 (v75.4.0 removed Python 3.8 support).

If developers change project.license = "Apache-2.0" as the warning suggested (emitted on a Python >=3.9 environment).

  [project]
  requires-python = ">= 3.8"
- license = { text = "Apache-2.0" }
+ license = "Apache-2.0"

The build will break on Python 3.8 due to the lack of PEP-639-supported setuptools (maximum is v75.3.2).

Processing /.../xxx
  Installing build dependencies ... done
  Getting requirements to build wheel ... error
  error: subprocess-exited-with-error
  
  × Getting requirements to build wheel did not run successfully.
  │ exit code: 1
  ╰─> [87 lines of output]
      configuration error: `project.license` must be valid exactly by one definition (2 matches found):
      
          - keys:
              'file': {type: string}
            required: ['file']
          - keys:
              'text': {type: string}
            required: ['text']
      
      DESCRIPTION:
          `Project license <https://peps.python.org/pep-0621/#license>`_.
      
      GIVEN VALUE:
          "Apache-2.0"
      
      OFFENDING RULE: 'oneOf'
      
      DEFINITION:
          {
              "oneOf": [
                  {
                      "properties": {
                          "file": {
                              "type": "string",
                              "$$description": [
                                  "Relative path to the file (UTF-8) which contains the license for the",
                                  "project."
                              ]
                          }
                      },
                      "required": [
                          "file"
                      ]
                  },
                  {
                      "properties": {
                          "text": {
                              "type": "string",
                              "$$description": [
                                  "The license of the project whose meaning is that of the",
                                  "`License field from the core metadata",
                                  "<https://packaging.python.org/specifications/core-metadata/#license>`_."
                              ]
                          }
                      },
                      "required": [
                          "text"
                      ]
                  }
              ]
          }

OS / Environment

Not related.

Additional Information

pyproject.toml is a static configuration file. It is impossible for packages that can support multiple Python versions (including EOLs) and also do not emit warnings during building. The warning can suggest developers use setup.py but I don't think it is encouraged since they are already using pyproject.toml.

I'd suggest to do not deprecate TOML-table-based project.license with only a text key. Make the following equivalent:

[project]
license = { text = "SPDX expression" }
[project]
license = "SPDX expression"

Code of Conduct

  • I agree to follow the PSF Code of Conduct
@XuehaiPan XuehaiPan added documentation Needs Triage Issues that need to be evaluated for severity and status. labels Mar 20, 2025
@abravalheri
Copy link
Contributor

abravalheri commented Mar 20, 2025

Please note that it is not in the scope of this project to support use cases involving discontinued Python versions.

Python 3.8 reached EoL in 2024, so use cases involving 3.8, are no longer supported by setuptools dev team.

If you wish to use the same codebase to support 3.8 and the following Python versions, I suppose you have a couple of options:

  • Use the same (old) version of setuptools across the board and only do the changes when you can stop publishing your package for 3.8
  • Use dynamic and solve the license specification in setup.py
  • Live with the deprecated warning and plan discontinuing your package for 3.8 before the deprecation due date.
  • Remove license expressions from the configuration files and instead rely on the automatic license file inclusion for the well-known license file paths (e.g., LICENSE at the root of the project). You would still be distributing license info in the form of license files (which at the end of the day is what counts... SPDX indexes are at the best an approximate proxy for the license file, re-distributors would still have to read the file themselves).

These suggestions are untested. Please make sure to verify and test before adopting.

@XuehaiPan
Copy link
Author

XuehaiPan commented Mar 20, 2025

Note this issue does not only exist for Python 3.8 but also all existing Python packages using setuptools. The string-based project.license is not backward-compatible (see the error in #4903 (comment)). Developers need to pin build-system.requires if they want to migrate to PEP 639:

[build-system]
requires = ["setuptools >= 77"]
build-backend = "setuptools.build_meta"

[project]
license = "SPDX expression"

However, this ideal setting will not work in the real world. For example, there are many use cases to use pip install --no-build-isolation due to limited internet connection (on a server) or build-system.requires contains big dependency (e.g., torch). The installer will not upgrade setuptools to the PEP 639 supported version (>= 77). A PEP-639-migrated package will break on the user side. Developers have to revert the migration.

Due to this limitation, downstream developers will postpone the PEP 639 migration. The old code will exist until the day the compact layer for table-based project.license is removed and breaks developers' CI.

@XuehaiPan XuehaiPan changed the title [Docs] Need migration guide for license expression (PEP 639) on Python <= 3.8 (TOML-table-based project.license deprecation) [Docs] Need migration guide for license expression (PEP 639) about TOML-table-based project.license deprecation if v77 unavailable Mar 20, 2025
@abravalheri abravalheri closed this as not planned Won't fix, can't repro, duplicate, stale Mar 20, 2025
@abravalheri
Copy link
Contributor

abravalheri commented Mar 20, 2025

If users want to make sure a version of setuptools that supports a certain feature is being picked up by the build frontend, it does make sense to use a >= for the version specification.

If the environment cannot download the version of the build requirements on the spot, then it needs to be pre-prepared beforehand.

These are normal activities when working on builds, not specific to this release. So in a sense, there is no need for a special guide for v77. The same arguments can be used for the whole parsing of pyproject.toml or setup.cfg before that. People usually omit the >= part because very often the build frontend will download the latest version of setuptools. If they need builds with special requirements (like lack of internet or opt-out of build isolation), then they need to be more careful and ensure the environment is properly set.

If you would to submit a docs PR with improvements that is very welcome, but other than, the release in question is not different than other features previously released.

@abravalheri abravalheri reopened this Mar 20, 2025
@abravalheri abravalheri added enhancement help wanted and removed Needs Triage Issues that need to be evaluated for severity and status. labels Mar 20, 2025
@abravalheri
Copy link
Contributor

abravalheri commented Mar 20, 2025

#4904 documents the version introducing PEP 639, so the information is available in the docs and warnings.

Further PRs for documentation improvements and suggestions are welcome.

@XuehaiPan
Copy link
Author

XuehaiPan commented Mar 21, 2025

#4904 documents the version introducing PEP 639, so the information is available in the docs and warnings.

Thanks! A user-friendly warning can prompt the developers to add a command to upgrade setuptools in their installation guide:

python3 -m pip install --upgrade pip setuptools
python3 -m pip git+https://github.com/owner/some-package
python3 -m pip another-package

Further PRs for documentation improvements and suggestions are welcome.

I wonder if it is suitable to automatically convert project.license = { text = "SPDX" } to project.license = "SPDX". I can submit a PR to implement this if accepted.

def _license(dist: Distribution, val: str | dict, root_dir: StrPath | None):
from setuptools.config import expand
if isinstance(val, str):
if getattr(dist.metadata, "license", None):
SetuptoolsWarning.emit("`license` overwritten by `pyproject.toml`")
dist.metadata.license = None
_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)
_set_config(dist, "license", _static.Str(value))
dist._referenced_files.add(val["file"])
else:
_set_config(dist, "license", _static.Str(val["text"]))

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

    if isinstance(val, dict) and set(val) == {'text'} and isinstance(val["text"], str):
        # This is for backward compatibility with the old table-based `license` field.
        # Convert the old table-based `license` to a string (PEP 639) if it only contains `text`
        # and no `file` field.
        # E.g. `license = {text = "MIT"}` to `license = "MIT"`
        from ._validate_pyproject.formats import SPDX

        if SPDX(val['text']):
            val = val['text']

    if isinstance(val, str):
        ...
    else:
        ...

The deprecation warning will be still emitted on:

[project]
license = { text = "non SPDX expression" }

or

[project]
license = { file = "/path/to/file" }

@abravalheri
Copy link
Contributor

abravalheri commented Mar 21, 2025

A user-friendly warning can prompt the developers to add a command to upgrade setuptools in their installation guide:

Adding this instruction could be confusing for general users.
Most of the users don't need setuptools to be installed. And very often it is good that they don't (e.g. to avoid errors when the build isolation from frontend tools are leaky).
The default behaviour of build tools when the file pyproject.toml file exists is to download the build dependencies on the spot.

People that need to use --no-build-isolation (which is not the default) need to add this information to their own package README, because they require special circumstances to build.


I wonder if it is suitable to automatically convert project.license = { text = "SPDX" } to project.license = "SPDX".

The thing is that not all project.license.text are SPDX indices, and also some of them may coincidentally be parseable as an SPDX index but the developer may not want it to be interpreted as such. For example, a project may have project.license.text = "MIT", because the original code itself is MIT, but the project may contain bundled code under GPL: while "MIT" is parseable, the actual SPDX index would be "MIT AND GPL-3.0-or-later".

The reason why we have a warning is that we are prompting the user with a call to action, since it is not trivial and immediate to convert between one and the other. I think the best in the case is to refuse to guess, given that it is a form of legal information.

@vanschelven
Copy link

Please note that it is not in the scope of this project to support use cases involving discontinued Python versions.

Is it in the scope of the project to support use cases involving backwards compatibility across the project itself? Debian's Trixie comes with setuptools 75.8.0-1. This means that there is currently no documented way to package something for both modern Python versions and stable Debian in a uniform way AFAIU.

@vanschelven
Copy link

The present issue was opened on Mar 20; a few days later we had #4910 "breaking the internet". The present issue is about a different field, but the consequences would seem to be similar. Does this mean the present issue should also be seen in a different light?

Related:

@anntzer
Copy link

anntzer commented Apr 5, 2025

For me, because of the issue here (impossibility to support old Python versions) as well as #4938, PEP639 is going to have the opposite effect than what it intended (improving metadata): I plan to just strip out all license metadata of my packages and just rely on the shipped LICENSE.txt as legal info.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants