PEP 639: Move ancillary parts to separate pages (#3727)

This commit is contained in:
Karolina Surma 2024-04-06 20:23:15 +02:00 committed by GitHub
parent f8f3ec0be2
commit b295ab338c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 1751 additions and 1691 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,217 @@
:orphan:
Appendix: Licensing Examples
============================
Abstract
--------
This document contains guidance on PEP 639 application in the
real-life :ref:`examples <639-examples>`.
.. _639-examples:
Examples
--------
.. _639-example-basic:
Basic example
'''''''''''''
The Setuptools project itself, as of `version 59.1.1 <setuptools5911_>`__,
does not use the ``License`` field in its own project source metadata.
Further, it no longer explicitly specifies ``license_file``/``license_files``
as it did previously, since Setuptools relies on its own automatic
inclusion of license-related files matching common patterns,
such as the ``LICENSE`` file it uses.
It includes the following license-related metadata in its ``setup.cfg``:
.. code-block:: ini
[metadata]
classifiers =
License :: OSI Approved :: MIT License
The simplest migration to PEP 639 would consist of using this instead:
.. code-block:: ini
[metadata]
license_expression = MIT
Or, in the ``[project]`` table of ``pyproject.toml``:
.. code-block:: toml
[project]
license = "MIT"
The output core metadata for the distribution packages would then be:
.. code-block:: email
License-Expression: MIT
License-File: LICENSE
The ``LICENSE`` file would be stored at ``/setuptools-${VERSION}/LICENSE``
in the sdist and ``/setuptools-${VERSION}.dist-info/licenses/LICENSE``
in the wheel, and unpacked from there into the site directory (e.g.
``site-packages``) on installation; ``/`` is the root of the respective archive
and ``${VERSION}`` the version of the Setuptools release in the core metadata.
.. _639-example-advanced:
Advanced example
''''''''''''''''
Suppose Setuptools were to include the licenses of the third-party projects
that are vendored in the ``setuptools/_vendor/`` and ``pkg_resources/_vendor``
directories; specifically:
.. code-block:: text
packaging==21.2
pyparsing==2.2.1
ordered-set==3.1.1
more_itertools==8.8.0
The license expressions for these projects are:
.. code-block:: text
packaging: Apache-2.0 OR BSD-2-Clause
pyparsing: MIT
ordered-set: MIT
more_itertools: MIT
A comprehensive license expression covering both Setuptools
proper and its vendored dependencies would contain these metadata,
combining all the license expressions into one. Such an expression might be:
.. code-block:: text
MIT AND (Apache-2.0 OR BSD-2-Clause)
In addition, per the requirements of the licenses, the relevant license files
must be included in the package. Suppose the ``LICENSE`` file contains the text
of the MIT license and the copyrights used by Setuptools, ``pyparsing``,
``more_itertools`` and ``ordered-set``; and the ``LICENSE*`` files in the
``setuptools/_vendor/packaging/`` directory contain the Apache 2.0 and
2-clause BSD license text, and the Packaging copyright statement and
`license choice notice <packaginglicense_>`__.
Specifically, we assume the license files are located at the following
paths in the project source tree (relative to the project root and
``pyproject.toml``):
.. code-block:: ini
LICENSE
setuptools/_vendor/packaging/LICENSE
setuptools/_vendor/packaging/LICENSE.APACHE
setuptools/_vendor/packaging/LICENSE.BSD
Putting it all together, our ``setup.cfg`` would be:
.. code-block:: ini
[metadata]
license_expression = MIT AND (Apache-2.0 OR BSD-2-Clause)
license_files =
LICENSE
setuptools/_vendor/packaging/LICENSE
setuptools/_vendor/packaging/LICENSE.APACHE
setuptools/_vendor/packaging/LICENSE.BSD
In the ``[project]`` table of ``pyproject.toml``, with license files
specified explicitly via the ``paths`` subkey, this would look like:
.. code-block:: toml
[project]
license = "MIT AND (Apache-2.0 OR BSD-2-Clause)"
license-files.paths = [
"LICENSE",
"setuptools/_vendor/LICENSE",
"setuptools/_vendor/LICENSE.APACHE",
"setuptools/_vendor/LICENSE.BSD",
]
Or alternatively, matched via glob patterns, this could be:
.. code-block:: toml
[project]
license = "MIT AND (Apache-2.0 OR BSD-2-Clause)"
license-files.globs = [
"LICENSE*",
"setuptools/_vendor/LICENSE*",
]
With either approach, the output core metadata in the distribution
would be:
.. code-block:: email
License-Expression: MIT AND (Apache-2.0 OR BSD-2-Clause)
License-File: LICENSE
License-File: setuptools/_vendor/packaging/LICENSE
License-File: setuptools/_vendor/packaging/LICENSE.APACHE
License-File: setuptools/_vendor/packaging/LICENSE.BSD
In the resulting sdist, with ``/`` as the root of the archive and ``${VERSION}``
the version of the Setuptools release specified in the core metadata,
the license files would be located at the paths:
.. code-block:: shell
/setuptools-${VERSION}/LICENSE
/setuptools-${VERSION}/setuptools/_vendor/packaging/LICENSE
/setuptools-${VERSION}/setuptools/_vendor/packaging/LICENSE.APACHE
/setuptools-${VERSION}/setuptools/_vendor/packaging/LICENSE.BSD
In the built wheel, with ``/`` being the root of the archive and
``{version}`` as the previous, the license files would be stored at:
.. code-block:: shell
/setuptools-${VERSION}.dist-info/licenses/LICENSE
/setuptools-${VERSION}.dist-info/licenses/setuptools/_vendor/packaging/LICENSE
/setuptools-${VERSION}.dist-info/licenses/setuptools/_vendor/packaging/LICENSE.APACHE
/setuptools-${VERSION}.dist-info/licenses/setuptools/_vendor/packaging/LICENSE.BSD
Finally, in the installed project, with ``site-packages`` being the site dir
and ``{version}`` as the previous, the license files would be installed to:
.. code-block:: shell
site-packages/setuptools-${VERSION}.dist-info/licenses/LICENSE
site-packages/setuptools-${VERSION}.dist-info/licenses/setuptools/_vendor/packaging/LICENSE
site-packages/setuptools-${VERSION}.dist-info/licenses/setuptools/_vendor/packaging/LICENSE.APACHE
site-packages/setuptools-${VERSION}.dist-info/licenses/setuptools/_vendor/packaging/LICENSE.BSD
.. _639-example-expression:
Expression examples
'''''''''''''''''''
Some additional examples of valid ``License-Expression`` values:
.. code-block:: email
License-Expression: MIT
License-Expression: BSD-3-Clause
License-Expression: MIT AND (Apache-2.0 OR BSD-2-clause)
License-Expression: MIT OR GPL-2.0-or-later OR (FSFUL AND BSD-2-Clause)
License-Expression: GPL-3.0-only WITH Classpath-Exception-2.0 OR BSD-3-Clause
License-Expression: LicenseRef-Public-Domain OR CC0-1.0 OR Unlicense
License-Expression: LicenseRef-Proprietary
.. _packaginglicense: https://github.com/pypa/packaging/blob/21.2/LICENSE
.. _setuptools5911: https://github.com/pypa/setuptools/blob/v59.1.1/setup.cfg

View File

@ -0,0 +1,363 @@
:orphan:
Appendix: License Documentation in Python and Other Projects
============================================================
Abstract
--------
There are multiple ways used or recommended to document licenses.
This document contains the results of a comprehensive survey of license
documentation in Python and other languages.
.. _639-license-doc-python:
License Documentation in Python
-------------------------------
.. _639-license-doc-core-metadata:
Core metadata
'''''''''''''
There are two overlapping core metadata fields to document a license: the
license ``Classifier`` `strings <classifiers_>`__ prefixed with ``License ::``
and the ``License`` `field <licensefield_>`__ as free text.
The core metadata ``License`` field documentation is currently:
.. code-block:: rst
License
=======
.. versionadded:: 1.0
Text indicating the license covering the distribution where the license
is not a selection from the "License" Trove classifiers. See
:ref:`"Classifier" <metadata-classifier>` below.
This field may also be used to specify a
particular version of a license which is named via the ``Classifier``
field, or to indicate a variation or exception to such a license.
Examples::
License: This software may only be obtained by sending the
author a postcard, and then the user promises not
to redistribute it.
License: GPL version 3, excluding DRM provisions
Even though there are two fields, it is at times difficult to convey anything
but simpler licensing. For instance, some classifiers lack precision
(GPL without a version) and when multiple license classifiers are
listed, it is not clear if both licenses must apply, or the user may choose
between them. Furthermore, the list of available license classifiers
is rather limited and out-of-date.
.. _639-license-doc-setuptools-wheel:
Setuptools and Wheel
''''''''''''''''''''
Beyond a license code or qualifier, license text files are documented and
included in a built package either implicitly or explicitly,
and this is another possible source of confusion:
- In the `Setuptools <setuptoolssdist_>`__ and `Wheel <wheels_>`__ projects,
license files are automatically added to the distribution (at their source
location in a source distribution/sdist, and in the ``.dist-info``
directory of a built wheel) if they match one of a number of common license
file name patterns (``LICEN[CS]E*``, ``COPYING*``, ``NOTICE*`` and
``AUTHORS*``). Alternatively, a package author can specify a list of license
file paths to include in the built wheel under the ``license_files`` key in
the ``[metadata]`` section of the project's ``setup.cfg``, or as an argument
to the ``setuptools.setup()`` function. At present, following the Wheel
project's lead, Setuptools flattens the collected license files into the
metadata directory, clobbering files with the same name, and dumps license
files directly into the top-level ``.dist-info`` directory, but there is a
`desire to resolve both these issues <setuptoolsfiles_>`__,
contingent on PEP 639 being accepted.
- Both tools also support an older, singular ``license_file`` parameter that
allows specifying only one license file to add to the distribution, which
has been deprecated for some time but still sees `some use <pipsetup_>`__.
- Following the publication of an earlier draft of PEP 639, Setuptools
`added support <setuptoolspep639_>`__ for ``License-File`` in distribution
metadata as described in this specification. This allows other tools
consuming the resulting metadata to unambiguously locate the license file(s)
for a given package.
.. _639-license-doc-pypug:
PyPA Packaging Guide and Sample Project
'''''''''''''''''''''''''''''''''''''''
Both the `PyPA beginner packaging tutorial <packagingtuttxt_>`__ and its more
comprehensive `packaging guide <packagingguidetxt_>`__ state that it is
important that every package include a license file. They point to the
``LICENSE.txt`` in the official PyPA sample project as an example, which is
`explicitly listed <samplesetupcfg_>`__ under the ``license_files`` key in
its ``setup.cfg``, following existing practice formally specified by PEP 639.
Both the `beginner packaging tutorial <packagingtutkey_>`__ and the
`sample project <samplesetuppy_>`__ only use classifiers to declare a
package's license, and do not include or mention the ``License`` field.
The `full packaging guide <licensefield_>`__ does mention this field, but
states that authors should use the license classifiers instead, unless the
project uses a non-standard license (which the guide discourages).
.. _639-license-doc-source-files:
Python source code files
''''''''''''''''''''''''
**Note:** Documenting licenses in source code is not in the scope of PEP 639.
Beside using comments and/or ``SPDX-License-Identifier`` conventions, the
license is `sometimes <pycode_>`__ documented in Python code files using
a "dunder" module-level constant, typically named ``__license__``.
This convention, while perhaps somewhat antiquated, is recognized by the
built-in ``help()`` function and the standard ``pydoc`` module.
The dunder variable will show up in the ``help()`` DATA section for a module.
.. _639-license-doc-other-projects:
License Documentation in Other Projects
---------------------------------------
Linux distribution packages
'''''''''''''''''''''''''''
**Note:** in most cases, the texts of the most common licenses are included
globally in a shared documentation directory (e.g. ``/usr/share/doc``).
- Debian documents package licenses with
`machine readable copyright files <dep5_>`__.
It defines its own license expression syntax and list of identifiers for
common licenses, both of which are closely related to those of SPDX.
- `Fedora packages <fedora_>`__ specify how to include
`License Texts <fedoratext_>`__ and use a
`License field <fedoralicense_>`__ that must be filled
with appropriate short license identifier(s) from an extensive list
of `"Good Licenses" <fedoralist_>`__. Fedora also defines its own
license expression syntax, similar to that of SPDX.
- `OpenSUSE packages <opensuse_>`__ use SPDX license expressions with
SPDX license IDs and a
`list of additional license identifiers <opensuselist_>`__.
- `Gentoo ebuild <pycode_>`__ uses a ``LICENSE`` variable.
This field is specified in `GLEP-0023 <glep23_>`__ and in the
`Gentoo development manual <gentoodev_>`__.
Gentoo also defines a list of allowed licenses and a license expression
syntax, which is rather different from SPDX.
- The `FreeBSD package Makefile <freebsd_>`__ provides ``LICENSE`` and
``LICENSE_FILE`` fields with a list of custom license symbols. For
non-standard licenses, FreeBSD recommends using ``LICENSE=UNKNOWN`` and
adding ``LICENSE_NAME`` and ``LICENSE_TEXT`` fields, as well as sophisticated
``LICENSE_PERMS`` to qualify the license permissions and ``LICENSE_GROUPS``
to document a license grouping. The ``LICENSE_COMB`` allows documenting more
than one license and how they apply together, forming a custom license
expression syntax. FreeBSD also recommends the use of
``SPDX-License-Identifier`` in source code files.
- `Arch Linux PKGBUILD <archinux_>`__ defines its
`own license identifiers <archlinuxlist_>`__.
The value ``'unknown'`` can be used if the license is not defined.
- `OpenWRT ipk packages <openwrt_>`__ use the ``PKG_LICENSE`` and
``PKG_LICENSE_FILES`` variables and recommend the use of SPDX License
identifiers.
- `NixOS uses SPDX identifiers <nixos_>`__ and some extra license IDs
in its license field.
- GNU Guix (based on NixOS) has a single License field, uses its own
`license symbols list <guix_>`__ and specifies how to use one license or a
`list of them <guixlicense_>`__.
- `Alpine Linux packages <alpine_>`__ recommend using SPDX identifiers in the
license field.
Language and application packages
'''''''''''''''''''''''''''''''''
- In Java, `Maven POM <maven_>`__ defines a ``licenses`` XML tag with a list
of licenses, each with a name, URL, comments and "distribution" type.
This is not mandatory, and the content of each field is not specified.
- The `JavaScript NPM package.json <npm_>`__ uses a single license field with
a SPDX license expression, or the ``UNLICENSED`` ID if none is specified.
A license file can be referenced as an alternative using
``SEE LICENSE IN <filename>`` in the single ``license`` field.
- `Rubygems gemspec <gem_>`__ specifies either a single or list of license
strings. The relationship between multiple licenses in a
list is not specified. They recommend using SPDX license identifiers.
- `CPAN Perl modules <perl_>`__ use a single license field, which is either a
single or a list of strings. The relationship between the licenses in
a list is not specified. There is a list of custom license identifiers plus
these generic identifiers: ``open_source``, ``restricted``, ``unrestricted``,
``unknown``.
- `Rust Cargo <cargo_>`__ specifies the use of an SPDX license expression
(v2.1) in the ``license`` field. It also supports an alternative expression
syntax using slash-separated SPDX license identifiers, and there is also a
``license_file`` field. The `crates.io package registry <cratesio_>`__
requires that either ``license`` or ``license_file`` fields are set when
uploading a package.
- `PHP composer.json <composer_>`__ uses a ``license`` field with
an SPDX license ID or ``proprietary``. The ``license`` field is either a
single string with resembling the SPDX license expression syntax with
``and`` and ``or`` keywords; or is a list of strings if there is a
(disjunctive) choice of licenses.
- `NuGet packages <nuget_>`__ previously used only a simple license URL, but
now specify using a SPDX license expression and/or the path to a license
file within the package. The NuGet.org repository states that they only
accept license expressions that are "approved by the Open Source Initiative
or the Free Software Foundation."
- Go language modules ``go.mod`` have no provision for any metadata beyond
dependencies. Licensing information is left for code authors and other
community package managers to document.
- The `Dart/Flutter spec <flutter_>`__ recommends using a single ``LICENSE``
file that should contain all the license texts, each separated by a line
with 80 hyphens.
- The `JavaScript Bower <bower_>`__ ``license`` field is either a single string
or list of strings using either SPDX license identifiers, or a path/URL
to a license file.
- The `Cocoapods podspec <cocoapod_>`__ ``license`` field is either a single
string, or a mapping with ``type``, ``file`` and ``text`` keys.
This is mandatory unless there is a ``LICENSE``/``LICENCE`` file provided.
- `Haskell Cabal <cabal_>`__ accepts an SPDX license expression since
version 2.2. The version of the SPDX license list used is a function of
the Cabal version. The specification also provides a mapping between
legacy (pre-SPDX) and SPDX license Identifiers. Cabal also specifies a
``license-file(s)`` field that lists license files to be installed with
the package.
- `Erlang/Elixir mix/hex package <mix_>`__ specifies a ``licenses`` field as a
required list of license strings, and recommends using SPDX license
identifiers.
- `D Langanguage dub packages <dub_>`__ define their own list of license
identifiers and license expression syntax, similar to the SPDX standard.
- The `R Package DESCRIPTION <cran_>`__ defines its own sophisticated license
expression syntax and list of licenses identifiers. R has a unique way of
supporting specifiers for license versions (such as ``LGPL (>= 2.0, < 3)``)
in its license expression syntax.
Other ecosystems
''''''''''''''''
- The ``SPDX-License-Identifier`` `header <spdxid_>`__ is a simple
convention to document the license inside a file.
- The `Free Software Foundation (FSF) <fsf_>`__ promotes the use of
SPDX license identifiers for clarity in the `GPL <gnu_>`__ and other
versioned free software licenses.
- The Free Software Foundation Europe (FSFE) `REUSE project <reuse_>`__
promotes using ``SPDX-License-Identifier``.
- The `Linux kernel <linux_>`__ uses ``SPDX-License-Identifier``
and parts of the FSFE REUSE conventions to document its licenses.
- `U-Boot <uboot_>`__ spearheaded using ``SPDX-License-Identifier`` in code
and now follows the Linux approach.
- The Apache Software Foundation projects use `RDF DOAP <apache_>`__ with
a single license field pointing to SPDX license identifiers.
- The `Eclipse Foundation <eclipse_>`__ promotes using
``SPDX-license-Identifiers``.
- The `ClearlyDefined project <clearlydefined_>`__ promotes using SPDX
license identifiers and expressions to improve license clarity.
- The `Android Open Source Project <android_>`__ uses ``MODULE_LICENSE_XXX``
empty tag files, where ``XXX`` is a license code such as ``BSD``, ``APACHE``,
``GPL``, etc. It also uses a ``NOTICE`` file that contains license and
notice texts.
.. _alpine: https://wiki.alpinelinux.org/wiki/Creating_an_Alpine_package#license
.. _android: https://github.com/aosp-mirror/platform_external_tcpdump/blob/android-platform-12.0.0_r1/MODULE_LICENSE_BSD
.. _apache: https://svn.apache.org/repos/asf/allura/doap_Allura.rdf
.. _archinux: https://wiki.archlinux.org/title/PKGBUILD#license
.. _archlinuxlist: https://archlinux.org/packages/core/any/licenses/files/
.. _bower: https://github.com/bower/spec/blob/b00c4403e22e3f6177c410ed3391b9259687e461/json.md#license
.. _cabal: https://cabal.readthedocs.io/en/3.6/cabal-package.html?highlight=license#pkg-field-license
.. _cargo: https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata
.. _classifiers: https://pypi.org/classifiers
.. _clearlydefined: https://clearlydefined.io
.. _cocoapod: https://guides.cocoapods.org/syntax/podspec.html#license
.. _composer: https://getcomposer.org/doc/04-schema.md#license
.. _conda: https://docs.conda.io/projects/conda-build/en/stable/resources/define-metadata.html#about-section
.. _cran: https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Licensing
.. _cratesio: https://doc.rust-lang.org/cargo/reference/registries.html#publish
.. _dep5: https://dep-team.pages.debian.net/deps/dep5/
.. _dub: https://dub.pm/package-format-json.html#licenses
.. _eclipse: https://www.eclipse.org/legal/epl-2.0/faq.php
.. _fedora: https://docs.fedoraproject.org/en-US/packaging-guidelines/LicensingGuidelines/
.. _fedoralicense: https://docs.fedoraproject.org/en-US/packaging-guidelines/LicensingGuidelines/#_valid_license_short_names
.. _fedoralist: https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing#Good_Licenses
.. _fedoratext: https://docs.fedoraproject.org/en-US/packaging-guidelines/LicensingGuidelines/#_license_text
.. _flit: https://flit.readthedocs.io/en/stable/pyproject_toml.html
.. _flutter: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#adding-licenses-to-the-license-file
.. _freebsd: https://docs.freebsd.org/en/books/porters-handbook/makefiles/#licenses
.. _fsf: https://www.fsf.org/blogs/rms/rms-article-for-claritys-sake-please-dont-say-licensed-under-gnu-gpl-2
.. _gem: https://guides.rubygems.org/specification-reference/#license=
.. _gentoo: https://devmanual.gentoo.org/ebuild-writing/variables/index.html#license
.. _gentoodev: https://devmanual.gentoo.org/general-concepts/licenses/index.html
.. _glep23: https://www.gentoo.org/glep/glep-0023.html
.. _gnu: https://www.gnu.org/licenses/identify-licenses-clearly.html
.. _guix: https://git.savannah.gnu.org/cgit/guix.git/tree/guix/licenses.scm?h=v1.3.0
.. _guixlicense: https://guix.gnu.org/manual/en/html_node/package-Reference.html#index-license_002c-of-packages
.. _licensefield: https://packaging.python.org/guides/distributing-packages-using-setuptools/#license
.. _linux: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/license-rules.rst
.. _maven: https://maven.apache.org/pom.html#Licenses
.. _mix: https://hex.pm/docs/publish
.. _npm: https://docs.npmjs.com/cli/v8/configuring-npm/package-json#license
.. _nixos: https://github.com/NixOS/nixpkgs/blob/21.05/lib/licenses.nix
.. _nuget: https://docs.microsoft.com/en-us/nuget/reference/nuspec#licenseurl
.. _opensuse: https://en.opensuse.org/openSUSE:Packaging_guidelines#Licensing
.. _opensuselist: https://docs.google.com/spreadsheets/d/14AdaJ6cmU0kvQ4ulq9pWpjdZL5tkR03exRSYJmPGdfs/pub
.. _openwrt: https://openwrt.org/docs/guide-developer/packages#buildpackage_variables
.. _packagingguidetxt: https://packaging.python.org/guides/distributing-packages-using-setuptools/#license-txt
.. _packagingtutkey: https://packaging.python.org/tutorials/packaging-projects/#configuring-metadata
.. _packagingtuttxt: https://packaging.python.org/tutorials/packaging-projects/#creating-a-license
.. _pbr: https://docs.openstack.org/pbr/latest/user/features.html
.. _perl: https://metacpan.org/pod/CPAN::Meta::Spec#license
.. _pipsetup: https://github.com/pypa/pip/blob/21.3.1/setup.cfg#L114
.. _poetry: https://python-poetry.org/docs/pyproject/#license
.. _pycode: https://github.com/search?l=Python&q=%22__license__%22&type=Code
.. _reuse: https://reuse.software/
.. _samplesetupcfg: https://github.com/pypa/sampleproject/blob/3a836905fbd687af334db16b16c37cf51dcbc99c/setup.cfg
.. _samplesetuppy: https://github.com/pypa/sampleproject/blob/3a836905fbd687af334db16b16c37cf51dcbc99c/setup.py#L98
.. _setuptoolssdist: https://github.com/pypa/setuptools/pull/1767
.. _setuptoolsfiles: https://github.com/pypa/setuptools/issues/2739
.. _setuptoolspep639: https://github.com/pypa/setuptools/pull/2645
.. _spdxid: https://spdx.dev/ids/
.. _uboot: https://www.denx.de/wiki/U-Boot/Licensing
.. _wheels: https://github.com/pypa/wheel/blob/0.37.0/docs/user_guide.rst#including-license-files-in-the-generated-wheel-file

View File

@ -0,0 +1,105 @@
:orphan:
.. _639-spec-mapping-classifiers-identifiers:
Appendix: Mapping License Classifiers to SPDX Identifiers
=========================================================
Most single license classifiers (namely, all those not mentioned below)
map to a single valid SPDX license identifier,
allowing tools to infer the SPDX license identifier they correspond to,
both for use when analyzing and auditing packages,
and providing a semi-automated mechanism of filling the ``license`` key
or the ``License-Expression`` field
following the :ref:`PEP 639 specification <639-spec-converting-metadata>`.
Some legacy license classifiers intend to specify a particular license,
but do not specify the particular version or variant, leading to a
`critical ambiguity <classifierissue_>`__
as to their terms, compatibility and acceptability.
Tools MUST NOT attempt to automatically infer a ``License-Expression``
when one of these classifiers is used without affirmative user action:
- ``License :: OSI Approved :: Academic Free License (AFL)``
- ``License :: OSI Approved :: Apache Software License``
- ``License :: OSI Approved :: Apple Public Source License``
- ``License :: OSI Approved :: Artistic License``
- ``License :: OSI Approved :: BSD License``
- ``License :: OSI Approved :: GNU Affero General Public License v3``
- ``License :: OSI Approved :: GNU Free Documentation License (FDL)``
- ``License :: OSI Approved :: GNU General Public License (GPL)``
- ``License :: OSI Approved :: GNU General Public License v2 (GPLv2)``
- ``License :: OSI Approved :: GNU General Public License v3 (GPLv3)``
- ``License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)``
- ``License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)``
- ``License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)``
- ``License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)``
A comprehensive mapping of these classifiers to their possible specific
identifiers was `assembled by Dustin Ingram <badclassifiers_>`__, which tools
MAY use as a reference for the identifier selection options to offer users
when prompting the user to explicitly select the license identifier
they intended for their project.
.. note::
Several additional classifiers, namely the "or later" variants of
the AGPLv3, GPLv2, GPLv3 and LGPLv3, are also listed in the aforementioned
mapping, but unambiguously map to their respective licenses,
and so are not listed here.
However, LGPLv2 is included above, as it could ambiguously
refer to either the distinct v2.0 or v2.1 variants of that license.
In addition, for the various special cases, the following mappings are
considered canonical and normative for the purposes of this specification:
- Classifier ``License :: Public Domain`` MAY be mapped to the generic
``License-Expression: LicenseRef-Public-Domain``.
If tools do so, they SHOULD issue an informational warning encouraging
the use of more explicit and legally portable license identifiers,
such as those for the `CC0 1.0 license <cc0_>`__ (``CC0-1.0``),
the `Unlicense <unlicense_>`__ (``Unlicense``),
or the `MIT license <mitlicense_>`__ (``MIT``),
since the meaning associated with the term "public domain" is thoroughly
dependent on the specific legal jurisdiction involved,
some of which lack the concept entirely.
Alternatively, tools MAY choose to treat these classifiers as ambiguous.
- The generic and sometimes ambiguous classifiers:
- ``License :: Free For Educational Use``
- ``License :: Free For Home Use``
- ``License :: Free for non-commercial use``
- ``License :: Freely Distributable``
- ``License :: Free To Use But Restricted``
- ``License :: Freeware``
- ``License :: Other/Proprietary License``
MAY be mapped to the generic
``License-Expression: LicenseRef-Proprietary``,
but tools MUST issue a prominent, informative warning if they do so.
Alternatively, tools MAY choose to treat these classifiers as ambiguous.
- The generic and ambiguous classifiers ``License :: OSI Approved`` and
``License :: DFSG approved`` do not map to any license expression,
and thus tools SHOULD treat them as ambiguous, or if not MUST ignore them.
- The classifiers ``License :: GUST Font License 1.0`` and
``License :: GUST Font License 2006-09-30`` have no mapping to SPDX license
identifiers, and no PyPI package uses them as of 2022-07-09.
When multiple license classifiers are used, their relationship is ambiguous,
and it is typically not possible to determine if all the licenses apply or if
there is a choice that is possible among the licenses,
In this case, tools MUST NOT automatically infer a license expression,
unless one license classifier is a parent of the other,
i.e. the child contains all ``::``-delineated components of the parent,
in which case tools MAY ignore the parent classifier
but SHOULD issue an informative warning when doing so.
.. _badclassifiers: https://github.com/pypa/trove-classifiers/issues/17#issuecomment-385027197
.. _cc0: https://creativecommons.org/publicdomain/zero/1.0/
.. _classifierissue: https://github.com/pypa/trove-classifiers/issues/17
.. _mitlicense: https://opensource.org/licenses/MIT
.. _unlicense: https://unlicense.org/

View File

@ -0,0 +1,887 @@
:orphan:
.. _639-rejected-ideas-details:
Appendix: Rejected Ideas
========================
Abstract
--------
This document contains a list of the alternative ideas to the ones proposed
in PEP 639 with detailed explanations why they were rejected.
Core metadata fields
--------------------
Potential alternatives to the structure, content and deprecation of the
core metadata fields specified in :pep:`639`.
Re-use the ``License`` field
''''''''''''''''''''''''''''
Following `initial discussion <reusediscussion_>`__, earlier versions of
PEP 639 proposed re-using the existing ``License`` field, which tools would
attempt to parse as a SPDX license expression with a fallback to free text.
Initially, this would merely cause a warning (or even pass silently),
but would eventually be treated as an error by modern tooling.
This offered the potential benefit of greater backwards-compatibility,
easing the community into using SPDX license expressions while taking advantage
of packages that already have them (either intentionally or coincidentally),
and avoided adding yet another license-related field.
However, following substantial discussion, consensus was reached that a
dedicated ``License-Expression`` field was the preferred overall approach.
The presence of this field is an unambiguous signal that a package
intends it to be interpreted as a valid SPDX identifier, without the need
for complex and potentially erroneous heuristics, and allows tools to
easily and unambiguously detect invalid content.
This avoids both false positive (``License`` values that a package author
didn't explicitly intend as an explicit SPDX identifier, but that happen
to validate as one), and false negatives (expressions the author intended
to be valid SPDX, but due to a typo or mistake are not), which are otherwise
not clearly distinguishable from true positives and negatives, an ambiguity
at odds with the goals of PEP 639.
Furthermore, it allows both the existing ``License`` field and
the license classifiers to be more easily deprecated,
with tools able to cleanly distinguish between packages intending to
affirmatively conform to the updated specification in PEP 639 or not,
and adapt their behavior (warnings, errors, etc) accordingly.
Otherwise, tools would either have to allow duplicative and potentially
conflicting ``License`` fields and classifiers, or warn/error on the
substantial number of existing packages that have SPDX identifiers as the
value for the ``License`` field, intentionally or otherwise (e.g. ``MIT``).
Finally, it avoids changing the behavior of an existing metadata field,
and avoids tools having to guess the ``Metadata-Version`` and field behavior
based on its value rather than merely its presence.
While this would mean the subset of existing distributions containing
``License`` fields valid as SPDX license expressions wouldn't automatically be
recognized as such, this only requires appending a few characters to the key
name in the project's source metadata, and PEP 639 provides extensive
guidance on how this can be done automatically by tooling.
Given all this, it was decided to proceed with defining a new,
purpose-created field, ``License-Expression``.
Re-Use the ``License`` field with a value prefix
''''''''''''''''''''''''''''''''''''''''''''''''
As an alternative to the previous, prefixing SPDX license expressions with,
e.g. ``spdx:`` was suggested to reduce the ambiguity inherent in re-using
the ``License`` field. However, this effectively amounted to creating
a field within a field, and doesn't address all the downsides of
keeping the ``License`` field. Namely, it still changes the behavior of an
existing metadata field, requires tools to parse its value
to determine how to handle its content, and makes the specification and
deprecation process more complex and less clean.
Yet, it still shares a same main potential downside as just creating a new
field: projects currently using valid SPDX identifiers in the ``License``
field, intentionally or not, won't be automatically recognized, and requires
about the same amount of effort to fix, namely changing a line in the
project's source metadata. Therefore, it was rejected in favor of a new field.
Don't make ``License-Expression`` mutually exclusive
''''''''''''''''''''''''''''''''''''''''''''''''''''
For backwards compatibility, the ``License`` field and/or the license
classifiers could still be allowed together with the new
``License-Expression`` field, presumably with a warning. However, this
could easily lead to inconsistent, and at the very least duplicative
license metadata in no less than *three* different fields, which is
squarely contrary to the goals of PEP 639 of making the licensing story
simpler and unambiguous. Therefore, and in concert with clear community
consensus otherwise, this idea was soundly rejected.
Don't deprecate existing ``License`` field and classifiers
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Several community members were initially concerned that deprecating the
existing ``License`` field and classifiers would result in
excessive churn for existing package authors and raise the barrier to
entry for new ones, particularly everyday Python developers seeking to
package and publish their personal projects without necessarily caring
too much about the legal technicalities or being a "license lawyer".
Indeed, every deprecation comes with some non-zero short-term cost,
and should be carefully considered relative to the overall long-term
net benefit. And at the minimum, this change shouldn't make it more
difficult for the average Python developer to share their work under
a license of their choice, and ideally improve the situation.
Following many rounds of proposals, discussion and refinement,
the general consensus was clearly in favor of deprecating the legacy
means of specifying a license, in favor of "one obvious way to do it",
to improve the currently complex and fragmented story around license
documentation. Not doing so would leave three different un-deprecated ways of
specifying a license for a package, two of them ambiguous, less than
clear/obvious how to use, inconsistently documented and out of date.
This is more complex for all tools in the ecosystem to support
indefinitely (rather than simply installers supporting older packages
implementing previous frozen metadata versions), resulting in a non-trivial
and unbounded maintenance cost.
Furthermore, it leads to a more complex and confusing landscape for users with
three similar but distinct options to choose from, particularly with older
documentation, answers and articles floating around suggesting different ones.
Of the three, ``License-Expression`` is the simplest and clearest to use
correctly; users just paste in their desired license identifier, or select it
via a tool, and they're done; no need to learn about Trove classifiers and
dig through the list to figure out which one(s) apply (and be confused
by many ambiguous options), or figure out on their own what should go
in the ``license`` key (anything from nothing, to the license text,
to a free-form description, to the same SPDX identifier they would be
entering in the ``license`` key anyway, assuming they can
easily find documentation at all about it). In fact, this can be
made even easier thanks to the new field. For example, GitHub's popular
`ChooseALicense.com <choosealicense_>`__ links to how to add SPDX license
identifiers to the project source metadata of various languages that support
them right in the sidebar of every license page; the SPDX support in this
PEP enables adding Python to that list.
For current package maintainers who have specified a ``License`` or license
classifiers, PEP 639 only recommends warnings and prohibits errors for
all but publishing tools, which are allowed to error if their intended
distribution platform(s) so requires. Once maintainers are ready to
upgrade, for those already using SPDX license expressions (accidentally or not)
this only requires appending a few characters to the key name in the
project's source metadata, and for those with license classifiers that
map to a single unambiguous license, or another defined case (public domain,
proprietary), they merely need to drop the classifier and paste in the
corresponding license identifier. PEP 639 provides extensive guidance and
examples, as will other resources, as well as explicit instructions for
automated tooling to take care of this with no human changes needed.
More complex cases where license metadata is currently specified may
need a bit of human intervention, but in most cases tools will be able
to provide a list of options following the mappings in PEP 639, and
these are typically the projects most likely to be constrained by the
limitations of the existing license metadata, and thus most benefited
by the new fields in PEP 639.
Finally, for unmaintained packages, those using tools supporting older
metadata versions, or those who choose not to provide license metadata,
no changes are required regardless of the deprecation.
Don't mandate validating new fields on PyPI
'''''''''''''''''''''''''''''''''''''''''''
Previously, while PEP 639 did include normative guidelines for packaging
publishing tools (such as Twine), it did not provide specific guidance
for PyPI (or other package indices) as to whether and how they
should validate the ``License-Expression`` or ``License-File`` fields,
nor how they should handle using them in combination with the deprecated
``License`` field or license classifiers. This simplifies the specification
and either defers implementation on PyPI to a later PEP, or gives
discretion to PyPI to enforce the stated invariants, to minimize
disruption to package authors.
However, this had been left unstated from before the ``License-Expression``
field was separate from the existing ``License``, which would make
validation much more challenging and backwards-incompatible, breaking
existing packages. With that change, there was a clear consensus that
the new field should be validated from the start, guaranteeing that all
distributions uploaded to PyPI that declare core metadata version 2.4
or higher and have the ``License-Expression`` field will have a valid
expression, such that PyPI and consumers of its packages and metadata
can rely upon to follow the specification here.
The same can be extended to the new ``License-File`` field as well,
to ensure that it is valid and the legally required license files are
present, and thus it is lawful for PyPI, users and downstream consumers
to distribute the package. (Of course, this makes no *guarantee* of such
as it is ultimately reliant on authors to declare them, but it improves
assurance of this and allows doing so in the future if the community so
decides.) To be clear, this would not require that any uploaded distribution
have such metadata, only that if they choose to declare it per the new
specification in PEP 639, it is assured to be valid.
Source metadata ``license`` key
-------------------------------
Alternate possibilities related to the ``license`` key in the
``pyproject.toml`` project source metadata.
Add ``expression`` and ``files`` subkeys to table
'''''''''''''''''''''''''''''''''''''''''''''''''
A previous working draft of PEP 639 added ``expression`` and ``files`` subkeys
to the existing ``license`` table in the project source metadata, to parallel
the existing ``file`` and ``text`` subkeys. While this seemed perhaps the
most obvious approach at first glance, it had several serious drawbacks
relative to that ultimately taken here.
Most saliently, this means two very different types of metadata are being
specified under the same top-level key that require very different handling,
and furthermore, unlike the previous arrangement, the subkeys were not mutually
exclusive and can both be specified at once, and with some subkeys potentially
being dynamic and others static, and mapping to different core metadata fields.
Furthermore, this leads to a conflict with marking the key as ``dynamic``
(assuming that is intended to specify the ``[project]`` table keys,
as that PEP seems to imprecisely imply,
rather than core metadata fields), as either or both would have
to be treated as ``dynamic``.
Grouping both license expressions and license files under the same key
forces an "all or nothing" approach, and creates ambiguity as to user intent.
There are further downsides to this as well. Both users and tools would need to
keep track of which fields are mutually exclusive with which of the others,
greatly increasing cognitive and code complexity, and in turn the probability
of errors. Conceptually, juxtaposing so many different fields under the
same key is rather jarring, and leads to a much more complex mapping between
``[project]`` keys and core metadata fields, not in keeping with :pep:`621`.
This causes the ``[project]`` table naming and structure to diverge further
from both the core metadata and native formats of the various popular packaging
tools that use it. Finally, this results in the spec being significantly more
complex and convoluted to understand and implement than the alternatives.
The approach PEP 639 now takes, using the reserved top-level string value
of the ``license`` key, adding a new ``license-files`` key
and deprecating the ``license`` table subkeys (``text`` and ``file``),
avoids most of the issues identified above,
and results in a much clearer and cleaner design overall.
It allows ``license`` and ``license-files`` to be tagged
``dynamic`` independently, separates two independent types of metadata
(syntactically and semantically), restores a closer to 1:1 mapping of
``[project]`` table keys to core metadata fields,
and reduces nesting by a level for both.
Other than adding one extra key to the file, there was no significant
apparent downside to this latter approach, so it was adopted for PEP 639.
Add an ``expression`` subkey instead of a string value
''''''''''''''''''''''''''''''''''''''''''''''''''''''
Adding just an ``expression`` subkey to the ``license`` table,
instead of using the reserved top-level string value,
would be more explicit for readers and writers,
in line with PEP 639's goals.
However, it still has the downsides listed above
that are not specific to the inclusion of the ``files`` key.
Relative to a flat string value,
it adds verbosity, complexity and an extra level of nesting,
and requires users and tools to remember and handle
the mutual exclusivity of the subkeys
and remember which are deprecated and which are not,
instead of cleanly deprecating the table subkeys as a whole.
Furthermore, it is less clearly the "default" choice for modern use,
given users tend to gravitate toward the simplest and most obvious option.
Finally, it seems reasonable to follow the suggested guidance in :pep:`621`,
given the top-level string value was specifically reserved for this purpose.
Define a new top-level ``license-expression`` key
'''''''''''''''''''''''''''''''''''''''''''''''''
An earlier version of PEP 639 defined a new, top-level ``license-expression``
under the ``[project]`` table,
rather than using the reserved string value of the ``license`` key.
This was seen as clearer and more explicit for readers and writers,
in line with the goals of PEP 639.
Additionally, while differences from existing tool formats (and core metadata
field names) have precedent in :pep:`621`,
using a key with an identical name as in most/all current tools
to mean something different (and map to a different core metadata field),
with distinct and incompatible syntax and semantics, does not,
and could cause confusion and ambiguity for readers and authors.
Also, per the `project source metadata spec <pep621specdynamic_>`__,
this would allow separately marking the ``[project]`` keys
corresponding to the ``License`` and ``License-Expression`` metadata fields
as ``dynamic``,
avoiding a potential concern with back-filling the ``License`` field
from the ``License-Expression`` field as PEP 639 currently allows
without it as ``license`` as dynamic
(which would not be possible, since they both map to the same top-level key).
However, community consensus favored using
the top-level string value of the existing ``license`` key,
as :pep:`reserved for this purpose by PEP 621 <621#license>`:
A practical string value for the license key has been purposefully left
out to allow for a future PEP to specify support for SPDX expressions
(the same logic applies to any sort of "type" field specifying what
license the file or text represents).
This is shorter and simpler for users to remember and type,
avoids adding a new top-level key while taking advantage of an existing one,
guides users toward using a license expression as the default,
and follows what was envisioned in the original :pep:`621`.
Additionally, this allows cleanly deprecating the table values
without deprecating the key itself,
and makes them inherently mutually exclusive without users having to remember
and tools having to enforce it.
Finally, consistency with other tool formats and the underlying core metadata
was not considered a sufficient priority
to override the advantages of using the existing key,
and the ``dynamic`` concerns were mostly mitigated by
not specifying legacy license to license expression conversion at build time,
explicitly specifying backfilling the ``License`` field when not ``dynamic``,
and the fact that both fields are mutually exclusive,
so there is little practical need to distinguish which is dynamic.
Therefore, a top-level string value for ``license`` was adopted for PEP 639,
as an earlier working draft had temporarily specified.
Add a ``type`` key to treat ``text`` as expression
''''''''''''''''''''''''''''''''''''''''''''''''''
Instead of using the reserved top-level string value
of the ``license`` key in the ``[project]`` table,
one could add a ``type`` subkey to the ``license`` table
to control whether ``text`` (or a string value)
is interpreted as free-text or a license expression. This could make
backward compatibility a little more seamless, as older tools could ignore
it and always treat ``text`` as ``license``, while newer tools would
know to treat it as a license expression, if ``type`` was set appropriately.
Indeed, :pep:`621` seems to suggest something of this sort as a possible
alternative way that SPDX license expressions could be implemented.
However, all the same downsides as in the previous item apply here,
including greater complexity, a more complex mapping between the project
source metadata and core metadata and inconsistency between the presentation
in tool config, project source metadata and core metadata,
a much less clean deprecation, further bikeshedding over what to name it,
and inability to mark one but not the other as dynamic, among others.
In addition, while theoretically potentially a little easier in the short
term, in the long term it would mean users would always have to remember
to specify the correct ``type`` to ensure their license expression is
interpreted correctly, which adds work and potential for error; we could
never safety change the default while being confident that users
understand that what they are entering is unambiguously a license expression,
with all the false positive and false negative issues as above.
Therefore, for these as well as the same reasons this approach was rejected
for the core metadata in favor of a distinct ``License-Expression`` field,
we similarly reject this here in favor of
the reserved string value of the ``license`` key.
Must be marked dynamic to back-fill
'''''''''''''''''''''''''''''''''''
The ``license`` key in the ``pyproject.toml`` could be required to be
explicitly set to dynamic in order for the ``License`` core metadata field
to be automatically back-filled from
the top-level string value of the ``license`` key.
This would be more explicit that the filling will be done,
as strictly speaking the ``license`` key is not (and cannot be) specified in
``pyproject.toml``, and satisfies a stricter interpretation of the letter
of the previous :pep:`621` specification that PEP 639 revises.
However, this doesn't seem to be necessary, because it is simply using the
static, verbatim literal value of the ``license`` key, as specified
strictly in PEP 639. Therefore, any conforming tool can trivially,
deterministically and unambiguously derive this using only the static data
in the ``pyproject.toml`` file itself.
Furthermore, this actually adds significant ambiguity, as it means the value
could get filled arbitrarily by other tools, which would in turn compromise
and conflict with the value of the new ``License-Expression`` field, which is
why such is explicitly prohibited by PEP 639. Therefore, not marking it as
``dynamic`` will ensure it is only handled in accordance with PEP 639's
requirements.
Finally, users explicitly being told to mark it as ``dynamic``, or not, to
control filling behavior seems to be a bit of a mis-use of the ``dynamic``
field as apparently intended, and prevents tools from adapting to best
practices (fill, don't fill, etc) as they develop and evolve over time.
Source metadata ``license-files`` key
-------------------------------------
Alternatives considered for the ``license-files`` key in the
``pyproject.toml`` ``[project]`` table, primarily related to the
path/glob type handling.
Add a ``type`` subkey to ``license-files``
''''''''''''''''''''''''''''''''''''''''''
Instead of defining mutually exclusive ``paths`` and ``globs`` subkeys
of the ``license-files`` ``[project]`` table key, we could
achieve the same effect with a ``files`` subkey for the list and
a ``type`` subkey for how to interpret it. However, the latter offers no
real advantage over the former, in exchange for requiring more keystrokes,
verbosity and complexity, as well as less flexibility in allowing both,
or another additional subkey in the future, as well as the need to bikeshed
over the subkey name. Therefore, it was summarily rejected.
Only accept verbatim paths
''''''''''''''''''''''''''
Globs could be disallowed completely as values to the ``license-files``
key in ``pyproject.toml`` and only verbatim literal paths allowed.
This would ensure that all license files are explicitly specified, all
specified license files are found and included, and the source metadata
is completely static in the strictest sense of the term, without tools
having to inspect the rest of the project source files to determine exactly
what license files will be included and what the ``License-File`` values
will be. This would also modestly simplify the spec and tool implementation.
However, practicality once again beats purity here. Globs are supported and
used by many existing tools for finding license files, and explicitly
specifying the full path to every license file would be unnecessarily tedious
for more complex projects with vendored code and dependencies. More
critically, it would make it much easier to accidentally miss a required
legal file, silently rendering the package illegal to distribute.
Tools can still statically and consistently determine the files to be included,
based only on those glob patterns the user explicitly specified and the
filenames in the package, without installing it, executing its code or even
examining its files. Furthermore, tools are still explicitly allowed to warn
if specified glob patterns (including full paths) don't match any files.
And, of course, sdists, wheels and others will have the full static list
of files specified in their distribution metadata.
Perhaps most importantly, this would also preclude the currently specified
default value, as widely used by the current most popular tools, and thus
be a major break to backward compatibility, tool consistency, and safe
and sane default functionality to avoid unintentional license violations.
And of course, authors are welcome and encouraged to specify their license
files explicitly via the ``paths`` table subkey, once they are aware of it and
if it is suitable for their project and workflow.
Only accept glob patterns
'''''''''''''''''''''''''
Conversely, all ``license-files`` strings could be treated as glob patterns.
This would slightly simplify the spec and implementation, avoid an extra level
of nesting, and more closely match the configuration format of existing tools.
However, for the cost of a few characters, it ensures users are aware
whether they are entering globs or verbatim paths. Furthermore, allowing
license files to be specified as literal paths avoids edge cases, such as those
containing glob characters (or those confusingly or even maliciously similar
to them, as described in :pep:`672`).
Including an explicit ``paths`` value ensures that the resulting
``License-File`` metadata is correct, complete and purely static in the
strictest sense of the term, with all license paths explicitly specified
in the ``pyproject.toml`` file, guaranteed to be included and with an early
error should any be missing. This is not practical to do, at least without
serious limitations for many workflows, if we must assume the items
are glob patterns rather than literal paths.
This allows tools to locate them and know the exact values of the
``License-File`` core metadata fields without having to traverse the
source tree of the project and match globs, potentially allowing easier,
more efficient and reliable programmatic inspection and processing.
Therefore, given the relatively small cost and the significant benefits,
this approach was not adopted.
Infer whether paths or globs
''''''''''''''''''''''''''''
It was considered whether to simply allow specifying an array of strings
directly for the ``license-files`` key, rather than making it a table with
explicit ``paths`` and ``globs``. This would be somewhat simpler and avoid
an extra level of nesting, and more closely match the configuration format
of existing tools. However, it was ultimately rejected in favor of separate,
mutually exclusive ``paths`` and ``globs`` table subkeys.
In practice, it only saves six extra characters in the ``pyproject.toml``
(``license-files = [...]`` vs ``license-files.globs = [...]``), but allows
the user to more explicitly declare their intent, ensures they understand how
the values are going to be interpreted, and serves as an unambiguous indicator
for tools to parse them as globs rather than verbatim path literals.
This, in turn, allows for more appropriate, clearly specified tool
behaviors for each case, many of which would be unreliable or impossible
without it, to avoid common traps, provide more helpful feedback and
behave more sensibly and intuitively overall. These include, with ``paths``,
guaranteeing that each and every specified file is included and immediately
raising an error if one is missing, and with ``globs``, checking glob syntax,
excluding unwanted backup, temporary, or other such files (as current tools
already do), and optionally warning if a glob doesn't match any files.
This also avoids edge cases (e.g. paths that contain glob characters) and
reliance on heuristics to determine interpretation—the very thing PEP 639
seeks to avoid.
.. _639-license-files-allow-flat-array:
Also allow a flat array value
'''''''''''''''''''''''''''''
Initially, after deciding to define ``license-files`` as a table of ``paths``
and ``globs``, thought was given to making a top-level string array under the
``license-files`` key mean one or the other (probably ``globs``, to match most
current tools). This is slightly shorter and simpler, would allow gently
nudging users toward a preferred one, and allow a slightly cleaner handling of
the empty case (which, at present, is treated identically for either).
However, this again only saves six characters in the best case, and there
isn't an obvious choice; whether from a perspective of preference (both had
clear use cases and benefits), nor as to which one users would naturally
assume.
Flat may be better than nested, but in the face of ambiguity, users
may not resist the temptation to guess. Requiring users to explicitly specify
one or the other ensures they are aware of how their inputs will be handled,
and is more readable for others, both human and machine alike. It also makes
the spec and tool implementation slightly more complicated, and it can always
be added in the future, but not removed without breaking backward
compatibility. And finally, for the "preferred" option, it means there is
more than one obvious way to do it.
Therefore, per :pep:`20`, the Zen of Python, this approach is hereby rejected.
Allow both ``paths`` and ``globs`` subkeys
''''''''''''''''''''''''''''''''''''''''''
Allowing both ``paths`` and ``globs`` subkeys to be specified under the
``license-files`` table was considered, as it could potentially allow
more flexible handling for particularly complex projects, and specify on a
per-pattern rather than overall basis whether ``license-files`` entries
should be treated as ``paths`` or ``globs``.
However, given the existing proposed approach already matches or exceeds the
power and capabilities of those offered in tools' config files, there isn't
clear demand for this and few likely cases that would benefit, it adds a large
amount of complexity for relatively minimal gain, in terms of the
specification, in tool implementations and in ``pyproject.toml`` itself.
There would be many more edge cases to deal with, such as how to handle files
matched by both lists, and it conflicts in multiple places with the current
specification for how tools should behave with one or the other, such as when
no files match, guarantees of all files being included and of the file paths
being explicitly, statically specified, and others.
Like the previous, if there is a clear need for it, it can be always allowed
in the future in a backward-compatible manner (to the extent it is possible
in the first place), while the same is not true of disallowing it.
Therefore, it was decided to require the two subkeys to be mutually exclusive.
Rename ``paths`` subkey to ``files``
''''''''''''''''''''''''''''''''''''
Initially, it was considered whether to name the ``paths`` subkey of the
``license-files`` table ``files`` instead. However, ``paths`` was ultimately
chosen, as calling the table subkey ``files`` resulted in duplication between
the table name (``license-files``) and the subkey name (``files``), i.e.
``license-files.files = ["LICENSE.txt"]``, made it seem like the preferred/
default subkey when it was not, and lacked the same parallelism with ``globs``
in describing the format of the string entry rather than what was being
pointed to.
Must be marked dynamic to use defaults
''''''''''''''''''''''''''''''''''''''
It may seem outwardly sensible, at least with a particularly restrictive
interpretation of :pep:`621`'s description of the ``dynamic`` list, to
consider requiring the ``license-files`` key to be explicitly marked as
``dynamic`` in order for the default glob patterns to be used, or alternatively
for license files to be matched and included at all.
However, this is merely declaring a static, strictly-specified default value
for this particular key, required to be used exactly by all conforming tools
(so long as it is not marked ``dynamic``, negating this argument entirely),
and is no less static than any other set of glob patterns the user themself
may specify. Furthermore, the resulting ``License-File`` core metadata values
can still be determined with only a list of files in the source, without
installing or executing any of the code, or even inspecting file contents.
Moreover, even if this were not so, practicality would trump purity, as this
interpretation would be strictly backwards-incompatible with the existing
format, and be inconsistent with the behavior with the existing tools.
Further, this would create a very serious and likely risk of a large number of
projects unknowingly no longer including legally mandatory license files,
making their distribution technically illegal, and is thus not a sane,
much less sensible default.
Finally, aside from adding an additional line of default-required boilerplate
to the file, not defining the default as dynamic allows authors to clearly
and unambiguously indicate when their build/packaging tools are going to be
handling the inclusion of license files themselves rather than strictly
conforming to the project source metadata portions of PEP 639;
to do otherwise would defeat the primary purpose of the ``dynamic`` list
as a marker and escape hatch.
License file paths
------------------
Alternatives related to the paths and locations of license files in the source
and built distributions.
Flatten license files in subdirectories
'''''''''''''''''''''''''''''''''''''''
Previous drafts of PEP 639 were silent on the issue of handling license files
in subdirectories. Currently, the `Wheel <wheelfiles_>`__ and (following its
example) `Setuptools <setuptoolsfiles_>`__ projects flatten all license files
into the ``.dist-info`` directory without preserving the source subdirectory
hierarchy.
While this is the simplest approach and matches existing ad hoc practice,
this can result in name conflicts and license files clobbering others,
with no obvious defined behavior for how to resolve them, and leaving the
package legally un-distributable without any clear indication to users that
their specified license files have not been included.
Furthermore, this leads to inconsistent relative file paths for non-root
license files between the source, sdist and wheel, and prevents the paths
given in the "static" ``[project]`` table metadata from being truly static,
as they need to be flattened, and may potentially overwrite one another.
Finally, the source directory structure often implies valuable information
about what the licenses apply to, and where to find them in the source,
which is lost when flattening them and far from trivial to reconstruct.
To resolve this, the PEP now proposes, as did contributors on both of the
above issues, reproducing the source directory structure of the original
license files inside the ``.dist-info`` directory. This would fully resolve the
concerns above, with the only downside being a more nested ``.dist-info``
directory. There is still a risk of collision with edge-case custom
filenames (e.g. ``RECORD``, ``METADATA``), but that is also the case
with the previous approach, and in fact with fewer files flattened
into the root, this would actually reduce the risk. Furthermore,
the following proposal rooting the license files under a ``licenses``
subdirectory eliminates both collisions and the clutter problem entirely.
Resolve name conflicts differently
''''''''''''''''''''''''''''''''''
Rather than preserving the source directory structure for license files
inside the ``.dist-info`` directory, we could specify some other mechanism
for conflict resolution, such as pre- or appending the parent directory name
to the license filename, traversing up the tree until the name was unique,
to avoid excessively nested directories.
However, this would not address the path consistency issues, would require
much more discussion, coordination and bikeshedding, and further complicate
the specification and the implementations. Therefore, it was rejected in
favor of the simpler and more obvious solution of just preserving the
source subdirectory layout, as many stakeholders have already advocated for.
Dump directly in ``.dist-info``
'''''''''''''''''''''''''''''''
Previously, the included license files were stored directly in the top-level
``.dist-info`` directory of built wheels and installed projects. This followed
existing ad hoc practice, ensured most existing wheels currently using this
feature will match new ones, and kept the specification simpler, with the
license files always being stored in the same location relative to the core
metadata regardless of distribution type.
However, this leads to a more cluttered ``.dist-info`` directory, littered
with arbitrary license files and subdirectories, as opposed to separating
licenses into their own namespace (which per the Zen of Python, :pep:`20`, are
"one honking great idea"). While currently small, there is still a
risk of collision with specific custom license filenames
(e.g. ``RECORD``, ``METADATA``) in the ``.dist-info`` directory, which
would only increase if and when additional files were specified here, and
would require carefully limiting the potential filenames used to avoid
likely conflicts with those of license-related files. Finally,
putting licenses into their own specified subdirectory would allow
humans and tools to quickly, easily and correctly list, copy and manipulate
all of them at once (such as in distro packaging, legal checks, etc)
without having to reference each of their paths from the core metadata.
Therefore, now is a prudent time to specify an alternate approach.
The simplest and most obvious solution, as suggested by several on the Wheel
and Setuptools implementation issues, is to simply root the license files
relative to a ``licenses`` subdirectory of ``.dist-info``. This is simple
to implement and solves all the problems noted here, without clear significant
drawbacks relative to other more complex options.
It does make the specification a bit more complex and less elegant, but
implementation should remain equally simple. It does mean that wheels
produced with following this change will have differently-located licenses
than those prior, but as this was already true for those in subdirectories,
and until PEP 639 there was no way of discovering these files or
accessing them programmatically, this doesn't seem likely to pose
significant problems in practice. Given this will be much harder if not
impossible to change later, once the status quo is standardized, tools are
relying on the current behavior and there is much greater uptake of not
only simply including license files but potentially accessing them as well
using the core metadata, if we're going to change it, now would be the time
(particularly since we're already introducing an edge-case change with how
license files in subdirs are handled, along with other refinements).
Therefore, the latter has been incorporated into current drafts of PEP 639.
Add new ``licenses`` category to wheel
''''''''''''''''''''''''''''''''''''''
Instead of defining a root license directory (``licenses``) inside
the core metadata directory (``.dist-info``) for wheels, we could instead
define a new category (and, presumably, a corresponding install scheme),
similar to the others currently included under ``.data`` in the wheel archive,
specifically for license files, called (e.g.) ``licenses``. This was mentioned
by the wheel creator, and would allow installing licenses somewhere more
platform-appropriate and flexible than just the ``.dist-info`` directory
in the site path, and potentially be conceptually cleaner than including
them there.
However, at present, PEP 639 does not implement this idea, and it is
deferred to a future one. It would add significant complexity and friction
to PEP 639, being primarily concerned with standardizing existing practice
and updating the core metadata specification. Furthermore, doing so would
likely require modifying ``sysconfig`` and the install schemes specified
therein, alongside Wheel, Installer and other tools, which would be a
non-trivial undertaking. While potentially slightly more complex for
repackagers (such as those for Linux distributions), the current proposal still
ensures all license files are included, and in a single dedicated directory
(which can easily be copied or relocated downstream), and thus should still
greatly improve the status quo in this regard without the attendant complexity.
In addition, this approach is not fully backwards compatible (since it
isn't transparent to tools that simply extract the wheel), is a greater
departure from existing practice and would lead to more inconsistent
license install locations from wheels of different versions. Finally,
this would mean licenses would not be installed as proximately to their
associated code, there would be more variability in the license root path
across platforms and between built distributions and installed projects,
accessing installed licenses programmatically would be more difficult, and a
suitable install location and method would need to be created, discussed
and decided that would avoid name clashes.
Therefore, to keep PEP 639 in scope, the current approach was retained.
Name the subdirectory ``license_files``
'''''''''''''''''''''''''''''''''''''''
Both ``licenses`` and ``license_files`` have been suggested as potential
names for the root license directory inside ``.dist-info`` of wheels and
installed projects. An initial draft of the PEP specified the former
due to being slightly clearer and consistent with the
name of the core metadata field (``License-File``)
and the ``[project]`` table key (``license-files``).
However, the current version of the PEP adopts the ``license`` name,
due to a general preference by the community for its shorter length,
greater simplicity and the lack of a separator character (``_``, ``-``, etc.).
Other ideas
-----------
Miscellaneous proposals, possibilities and discussion points that were
ultimately not adopted.
Map identifiers to license files
''''''''''''''''''''''''''''''''
This would require using a mapping (as two parallel lists would be too prone to
alignment errors), which would add extra complexity to how license
are documented and add an additional nesting level.
A mapping would be needed, as it cannot be guaranteed that all expressions
(keys) have a single license file associated with them (e.g.
GPL with an exception may be in a single file) and that any expression
does not have more than one. (e.g. an Apache license ``LICENSE`` and
its ``NOTICE`` file, for instance, are two distinct files).
For most common cases, a single license expression and one or more license
files would be perfectly adequate. In the rarer and more complex cases where
there are many licenses involved, authors can still safety use the fields
specified here, just with a slight loss of clarity by not specifying which
text file(s) map to which license identifier (though this should be clear in
practice given each license identifier has corresponding SPDX-registered
full license text), while not forcing the more complex data model
(a mapping) on the large majority of users who do not need or want it.
We could of course have a data field with multiple possible value types (it's a
string, it's a list, it's a mapping!) but this could be a source of confusion.
This is what has been done, for instance, in npm (historically) and in Rubygems
(still today), and as result tools need to test the type of the metadata field
before using it in code, while users are confused about when to use a list or a
string. Therefore, this approach is rejected.
Map identifiers to source files
'''''''''''''''''''''''''''''''
As discussed previously, file-level notices are out of scope for PEP 639,
and the existing ``SPDX-License-Identifier`` `convention <spdxid_>`__ can
already be used if this is needed without further specification here.
Don't freeze compatibility with a specific SPDX version
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
PEP 639 could omit specifying a specific SPDX specification version,
or one for the list of valid license identifiers, which would allow
more flexible updates as the specification evolves without another
PEP or equivalent.
However, serious concerns were expressed about a future SPDX update breaking
compatibility with existing expressions and identifiers, leaving current
packages with invalid metadata per the definition in PEP 639. Requiring
compatibility with a specific version of these specifications here
and a PEP or similar process to update it avoids this contingency,
and follows the practice of other packaging ecosystems.
Therefore, it was `decided <spdxversion_>`__ to specify a minimum version
and requires tools to be compatible with it, while still allowing updates
so long as they don't break backward compatibility. This enables
tools to immediate take advantage of improvements and accept new
licenses, but also remain backwards compatible with the version
specified here, balancing flexibility and compatibility.
.. _639-rejected-ideas-difference-license-source-binary:
Different licenses for source and binary distributions
''''''''''''''''''''''''''''''''''''''''''''''''''''''
As an additional use case, it was asked whether it was in scope for this
PEP to handle cases where the license expression for a binary distribution
(wheel) is different from that for a source distribution (sdist), such
as in cases of non-pure-Python packages that compile and bundle binaries
under different licenses than the project itself. An example cited was
`PyTorch <pytorch_>`__, which contains CUDA from Nvidia, which is freely
distributable but not open source. `NumPy <numpyissue_>`__ and
`SciPy <scipyissue_>`__ also had similar issues, as reported by the
original author of PEP 639 and now resolved for those cases.
However, given the inherent complexity here and a lack of an obvious
mechanism to do so, the fact that each wheel would need its own license
information, lack of support on PyPI for exposing license info on a
per-distribution archive basis, and the relatively niche use case, it was
determined to be out of scope for PEP 639, and left to a future PEP
to resolve if sufficient need and interest exists and an appropriate
mechanism can be found.
.. _choosealicense: https://choosealicense.com/
.. _numpyissue: https://github.com/numpy/numpy/issues/8689
.. _pep621specdynamic: https://packaging.python.org/en/latest/specifications/declaring-project-metadata/#dynamic
.. _pytorch: https://pypi.org/project/torch/
.. _reusediscussion: https://github.com/pombredanne/spdx-pypi-pep/issues/7
.. _scipyissue: https://github.com/scipy/scipy/issues/7093
.. _setuptoolsfiles: https://github.com/pypa/setuptools/issues/2739
.. _spdxid: https://spdx.dev/ids/
.. _spdxversion: https://github.com/pombredanne/spdx-pypi-pep/issues/6
.. _wheelfiles: https://github.com/pypa/wheel/issues/138

View File

@ -0,0 +1,160 @@
:orphan:
Appendix: User Scenarios
========================
Abstract
--------
This document contains guidance on PEP 639 application in the
real-life :ref:`user scenarios <639-user-scenarios>`.
.. _639-user-scenarios:
User Scenarios
--------------
The following covers the range of common use cases from a user perspective,
providing straightforward guidance for each. Do note that the following
should **not** be considered legal advice, and readers should consult a
licensed legal practitioner in their jurisdiction if they are unsure about
the specifics for their situation.
I have a private package that won't be distributed
''''''''''''''''''''''''''''''''''''''''''''''''''
If your package isn't shared publicly, i.e. outside your company,
organization or household, it *usually* isn't strictly necessary to include
a formal license, so you wouldn't necessarily have to do anything extra here.
However, it is still a good idea to include ``LicenseRef-Proprietary``
as a license expression in your package configuration, and/or a
copyright statement and any legal notices in a ``LICENSE.txt`` file
in the root of your project directory, which will be automatically
included by packaging tools.
I just want to share my own work without legal restrictions
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
While you aren't required to include a license, if you don't, no one has
`any permission to download, use or improve your work <dontchoosealicense_>`__,
so that's probably the *opposite* of what you actually want.
The `MIT license <mitlicense_>`__ is a great choice instead, as it's simple,
widely used and allows anyone to do whatever they want with your work
(other than sue you, which you probably also don't want).
To apply it, just paste `the text <chooseamitlicense_>`__ into a file named
``LICENSE.txt`` at the root of your repo, and add the year and your name to
the copyright line. Then, just add ``license = "MIT"`` under
``[project]`` in your ``pyproject.toml`` if your packaging tool supports it,
or in its config file/section (e.g. Setuptools ``license_expression = MIT``
under ``[metadata]`` in ``setup.cfg``). You're done!
I want to distribute my project under a specific license
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
To use a particular license, simply paste its text into a ``LICENSE.txt``
file at the root of your repo, if you don't have it in a file starting with
``LICENSE`` or ``COPYING`` already, and add
``license = "LICENSE-ID"`` under ``[project]`` in your
``pyproject.toml`` if your packaging tool supports it, or else in its
config file (e.g. for Setuptools, ``license_expression = LICENSE-ID``
under ``[metadata]`` in ``setup.cfg``). You can find the ``LICENSE-ID``
and copyable license text on sites like
`ChooseALicense <choosealicenselist_>`__ or `SPDX <spdxlist_>`__.
Many popular code hosts, project templates and packaging tools can add the
license file for you, and may support the expression as well in the future.
I maintain an existing package that's already licensed
''''''''''''''''''''''''''''''''''''''''''''''''''''''
If you already have license files and metadata in your project, you
should only need to make a couple of tweaks to take advantage of the new
functionality.
In your project config file, enter your license expression under
``license`` (``[project]`` table in ``pyproject.toml``),
``license_expression`` (Setuptools ``setup.cfg`` / ``setup.py``),
or the equivalent for your packaging tool,
and make sure to remove any legacy ``license`` table subkeys or
``License ::`` classifiers. Your existing ``license`` value may already
be valid as one (e.g. ``MIT``, ``Apache-2.0 OR BSD-2-Clause``, etc);
otherwise, check the `SPDX license list <spdxlist_>`__ for the identifier
that matches the license used in your project.
If your license files begin with ``LICENSE``, ``COPYING``, ``NOTICE`` or
``AUTHORS``, or you've already configured your packaging tool to add them
(e.g. ``license_files`` in ``setup.cfg``), you should already be good to go.
If not, make sure to list them under ``license-files.paths``
or ``license-files.globs`` under ``[project]`` in ``pyproject.toml``
(if your tool supports it), or else in your tool's configuration file
(e.g. ``license_files`` in ``setup.cfg`` for Setuptools).
See the :ref:`639-example-basic` for a simple but complete real-world demo
of how this works in practice, including some additional technical details.
Packaging tools may support automatically converting legacy licensing
metadata; check your tool's documentation for more information.
My package includes other code under different licenses
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
If your project includes code from others covered by different licenses,
such as vendored dependencies or files copied from other open source
software, you can construct a license expression (or have a tool
help you do so) to describe the licenses involved and the relationship
between them.
In short, ``License-1 AND License-2`` mean that *both* licenses apply
to your project, or parts of it (for example, you included a file
under another license), and ``License-1 OR License-2`` means that
*either* of the licenses can be used, at the user's option (for example,
you want to allow users a choice of multiple licenses). You can use
parenthesis (``()``) for grouping to form expressions that cover even the most
complex situations.
In your project config file, enter your license expression under
``license`` (``[project]`` table of ``pyproject.toml``),
``license_expression`` (Setuptools ``setup.cfg`` / ``setup.py``),
or the equivalent for your packaging tool,
and make sure to remove any legacy ``license`` table subkeys
or ``License ::`` classifiers.
Also, make sure you add the full license text of all the licenses as files
somewhere in your project repository. If all of them are in the root directory
and begin with ``LICENSE``, ``COPYING``, ``NOTICE`` or ``AUTHORS``,
they will be included automatically. Otherwise, you'll need to list the
relative path or glob patterns to each of them under ``license-files.paths``
or ``license-files.globs`` under ``[project]`` in ``pyproject.toml``
(if your tool supports it), or else in your tool's configuration file
(e.g. ``license_files`` in ``setup.cfg`` for Setuptools).
As an example, if your project was licensed MIT but incorporated
a vendored dependency (say, ``packaging``) that was licensed under
either Apache 2.0 or the 2-clause BSD, your license expression would
be ``MIT AND (Apache-2.0 OR BSD-2-Clause)``. You might have a
``LICENSE.txt`` in your repo root, and a ``LICENSE-APACHE.txt`` and
``LICENSE-BSD.txt`` in the ``_vendor`` subdirectory, so to include
all of them, you'd specify ``["LICENSE.txt", "_vendor/packaging/LICENSE*"]``
as glob patterns, or
``["LICENSE.txt", "_vendor/LICENSE-APACHE.txt", "_vendor/LICENSE-BSD.txt"]``
as literal file paths.
See a fully worked out :ref:`639-example-advanced` for a comprehensive end-to-end
application of this to a real-world complex project, with copious technical
details, and consult a `tutorial <spdxtutorial_>`__ for more help and examples
using SPDX identifiers and expressions.
.. _chooseamitlicense: https://choosealicense.com/licenses/mit/
.. _choosealicenselist: https://choosealicense.com/licenses/
.. _dontchoosealicense: https://choosealicense.com/no-permission/
.. _mitlicense: https://opensource.org/licenses/MIT
.. _spdxlist: https://spdx.org/licenses/
.. _spdxtutorial: https://github.com/david-a-wheeler/spdx-tutorial