diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 974699dc24..71f8813923 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 75.3.0 +current_version = 75.5.0 commit = True tag = True diff --git a/NEWS.rst b/NEWS.rst index 39bd36de66..25dac78e1d 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,47 @@ +v75.5.0 +======= + +Features +-------- + +- Removed support for ``SETUPTOOLS_DANGEROUSLY_SKIP_PYPROJECT_VALIDATION``, as it + is deemed prone to errors. (#4746) + + +v75.4.0 +======= + +Features +-------- + +- Added support for the environment variable + ``SETUPTOOLS_DANGEROUSLY_SKIP_PYPROJECT_VALIDATION=true``, allowing users to bypass + the validation of ``pyproject.toml``. + This option should be used only as a last resort when resolving dependency + issues, as it may lead to improper functioning. + Users who enable this setting are responsible for ensuring that ``pyproject.toml`` + complies with setuptools requirements. (#4611) + + .. attention:: + This environment variable was removed in a later version of ``setuptools``. + +- Require Python 3.9 or later. (#4718) +- Remove dependency on ``importlib_resources`` + and the vendored copy of the library. + Instead, ``setuptools`` consistently rely on stdlib's ``importlib.resources`` + (available on Python 3.9+). (#4718) +- Setuptools' ``bdist_wheel`` implementation no longer produces wheels with + the ``m`` SOABI flag (pymalloc-related). + This flag was removed on Python 3.8+ (see :obj:`sys.abiflags`). (#4718) +- Updated vendored packaging version to 24.2. (#4740) + + +Bugfixes +-------- + +- Merge with pypa/distutils@251797602, including fix for dirutil.mkpath handling in pypa/distutils#304. + + v75.3.0 ======= diff --git a/newsfragments/4718.feature.1.rst b/newsfragments/4718.feature.1.rst deleted file mode 100644 index f171cdde84..0000000000 --- a/newsfragments/4718.feature.1.rst +++ /dev/null @@ -1 +0,0 @@ -Require Python 3.9 or later. diff --git a/newsfragments/4718.feature.2.rst b/newsfragments/4718.feature.2.rst deleted file mode 100644 index 7c32c13b61..0000000000 --- a/newsfragments/4718.feature.2.rst +++ /dev/null @@ -1,4 +0,0 @@ -Remove dependency on ``importlib_resources`` -and the vendored copy of the library. -Instead, ``setuptools`` consistently rely on stdlib's ``importlib.resources`` -(available on Python 3.9+). diff --git a/newsfragments/4718.feature.3.rst b/newsfragments/4718.feature.3.rst deleted file mode 100644 index 9c80da9064..0000000000 --- a/newsfragments/4718.feature.3.rst +++ /dev/null @@ -1,3 +0,0 @@ -Setuptools' ``bdist_wheel`` implementation no longer produces wheels with -the ``m`` SOABI flag (pymalloc-related). -This flag was removed on Python 3.8+ (see :obj:`sys.abiflags`). diff --git a/pyproject.toml b/pyproject.toml index ca09a084d8..60ea8661b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ backend-path = ["."] [project] name = "setuptools" -version = "75.3.0" +version = "75.5.0" authors = [ { name = "Python Packaging Authority", email = "distutils-sig@python.org" }, ] @@ -41,7 +41,7 @@ test = [ "virtualenv>=13.0.0", "wheel>=0.44.0", # Consistent requirement normalisation in METADATA (see #4547) "pip>=19.1", # For proper file:// URLs support. - "packaging>=23.2", + "packaging>=24.2", "jaraco.envs>=2.2", "pytest-xdist>=3", # Dropped dependency on pytest-fork and py "jaraco.path>=3.2.0", @@ -90,7 +90,7 @@ doc = [ ssl = [] certs = [] core = [ - "packaging>=24", + "packaging>=24.2", "more_itertools>=8.8", "jaraco.text>=3.7", "importlib_metadata>=6; python_version < '3.10'", @@ -102,7 +102,7 @@ core = [ # for distutils "jaraco.collections", - "jaraco.functools", + "jaraco.functools>=4", "packaging", "more_itertools", ] @@ -134,8 +134,8 @@ type = [ # pin mypy version so a new version doesn't suddenly cause the CI to fail, # until types-setuptools is removed from typeshed. - # For help with static-typing issues, or mypy update, ping @Avasam - "mypy==1.12.*", + # For help with static-typing issues, or mypy update, ping @Avasam + "mypy>=1.12,<1.14", # Typing fixes in version newer than we require at runtime "importlib_metadata>=7.0.2; python_version < '3.10'", # Imported unconditionally in tools/finalize.py diff --git a/setuptools/_distutils/dir_util.py b/setuptools/_distutils/dir_util.py index 3b22839d27..d9782602cf 100644 --- a/setuptools/_distutils/dir_util.py +++ b/setuptools/_distutils/dir_util.py @@ -32,8 +32,9 @@ def wrap(self, func): def wrapper(path, *args, **kwargs): if path.absolute() in self: return + result = func(path, *args, **kwargs) self.add(path.absolute()) - return func(path, *args, **kwargs) + return result return wrapper @@ -44,7 +45,7 @@ def wrapper(path, *args, **kwargs): @functools.singledispatch @wrapper -def mkpath(name: pathlib.Path, mode=0o777, verbose=True, dry_run=False): +def mkpath(name: pathlib.Path, mode=0o777, verbose=True, dry_run=False) -> None: """Create a directory and any missing ancestor directories. If the directory already exists (or if 'name' is the empty string, which @@ -52,21 +53,15 @@ def mkpath(name: pathlib.Path, mode=0o777, verbose=True, dry_run=False): Raise DistutilsFileError if unable to create some directory along the way (eg. some sub-path exists, but is a file rather than a directory). If 'verbose' is true, log the directory created. - Return the list of directories actually created. """ if verbose and not name.is_dir(): log.info("creating %s", name) - ancestry = itertools.chain((name,), name.parents) - missing = (path for path in ancestry if not path.is_dir()) - try: dry_run or name.mkdir(mode=mode, parents=True, exist_ok=True) except OSError as exc: raise DistutilsFileError(f"could not create '{name}': {exc.args[-1]}") - return list(map(str, missing)) - @mkpath.register def _(name: str, *args, **kwargs): diff --git a/setuptools/_distutils/tests/test_dir_util.py b/setuptools/_distutils/tests/test_dir_util.py index 12e643ab74..fcc37ac568 100644 --- a/setuptools/_distutils/tests/test_dir_util.py +++ b/setuptools/_distutils/tests/test_dir_util.py @@ -1,6 +1,7 @@ """Tests for distutils.dir_util.""" import os +import pathlib import stat import unittest.mock as mock from distutils import dir_util, errors @@ -110,3 +111,24 @@ def test_copy_tree_exception_in_listdir(self): ): src = self.tempdirs[-1] dir_util.copy_tree(src, None) + + def test_mkpath_exception_uncached(self, monkeypatch, tmp_path): + """ + Caching should not remember failed attempts. + + pypa/distutils#304 + """ + + class FailPath(pathlib.Path): + def mkdir(self, *args, **kwargs): + raise OSError("Failed to create directory") + + target = tmp_path / 'foodir' + + with pytest.raises(errors.DistutilsFileError): + mkpath(FailPath(target)) + + assert not target.exists() + + mkpath(target) + assert target.exists() diff --git a/setuptools/_shutil.py b/setuptools/_shutil.py new file mode 100644 index 0000000000..6acbb4281f --- /dev/null +++ b/setuptools/_shutil.py @@ -0,0 +1,53 @@ +"""Convenience layer on top of stdlib's shutil and os""" + +import os +import stat +from typing import Callable, TypeVar + +from .compat import py311 + +from distutils import log + +try: + from os import chmod # pyright: ignore[reportAssignmentType] + # Losing type-safety w/ pyright, but that's ok +except ImportError: # pragma: no cover + # Jython compatibility + def chmod(*args: object, **kwargs: object) -> None: # type: ignore[misc] # Mypy reuses the imported definition anyway + pass + + +_T = TypeVar("_T") + + +def attempt_chmod_verbose(path, mode): + log.debug("changing mode of %s to %o", path, mode) + try: + chmod(path, mode) + except OSError as e: # pragma: no cover + log.debug("chmod failed: %s", e) + + +# Must match shutil._OnExcCallback +def _auto_chmod( + func: Callable[..., _T], arg: str, exc: BaseException +) -> _T: # pragma: no cover + """shutils onexc callback to automatically call chmod for certain functions.""" + # Only retry for scenarios known to have an issue + if func in [os.unlink, os.remove] and os.name == 'nt': + attempt_chmod_verbose(arg, stat.S_IWRITE) + return func(arg) + raise exc + + +def rmtree(path, ignore_errors=False, onexc=_auto_chmod): + """ + Similar to ``shutil.rmtree`` but automatically executes ``chmod`` + for well know Windows failure scenarios. + """ + return py311.shutil_rmtree(path, ignore_errors, onexc) + + +def rmdir(path, **opts): + if os.path.isdir(path): + rmtree(path, **opts) diff --git a/setuptools/_vendor/packaging-24.1.dist-info/INSTALLER b/setuptools/_vendor/packaging-24.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e38a..0000000000 --- a/setuptools/_vendor/packaging-24.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/setuptools/_vendor/packaging-24.1.dist-info/RECORD b/setuptools/_vendor/packaging-24.1.dist-info/RECORD deleted file mode 100644 index 2b1e6bd4db..0000000000 --- a/setuptools/_vendor/packaging-24.1.dist-info/RECORD +++ /dev/null @@ -1,37 +0,0 @@ -packaging-24.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -packaging-24.1.dist-info/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197 -packaging-24.1.dist-info/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174 -packaging-24.1.dist-info/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344 -packaging-24.1.dist-info/METADATA,sha256=X3ooO3WnCfzNSBrqQjefCD1POAF1M2WSLmsHMgQlFdk,3204 -packaging-24.1.dist-info/RECORD,, -packaging-24.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -packaging-24.1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 -packaging/__init__.py,sha256=dtw2bNmWCQ9WnMoK3bk_elL1svSlikXtLpZhCFIB9SE,496 -packaging/__pycache__/__init__.cpython-312.pyc,, -packaging/__pycache__/_elffile.cpython-312.pyc,, -packaging/__pycache__/_manylinux.cpython-312.pyc,, -packaging/__pycache__/_musllinux.cpython-312.pyc,, -packaging/__pycache__/_parser.cpython-312.pyc,, -packaging/__pycache__/_structures.cpython-312.pyc,, -packaging/__pycache__/_tokenizer.cpython-312.pyc,, -packaging/__pycache__/markers.cpython-312.pyc,, -packaging/__pycache__/metadata.cpython-312.pyc,, -packaging/__pycache__/requirements.cpython-312.pyc,, -packaging/__pycache__/specifiers.cpython-312.pyc,, -packaging/__pycache__/tags.cpython-312.pyc,, -packaging/__pycache__/utils.cpython-312.pyc,, -packaging/__pycache__/version.cpython-312.pyc,, -packaging/_elffile.py,sha256=_LcJW4YNKywYsl4169B2ukKRqwxjxst_8H0FRVQKlz8,3282 -packaging/_manylinux.py,sha256=Xo4V0PZz8sbuVCbTni0t1CR0AHeir_7ib4lTmV8scD4,9586 -packaging/_musllinux.py,sha256=p9ZqNYiOItGee8KcZFeHF_YcdhVwGHdK6r-8lgixvGQ,2694 -packaging/_parser.py,sha256=s_TvTvDNK0NrM2QB3VKThdWFM4Nc0P6JnkObkl3MjpM,10236 -packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431 -packaging/_tokenizer.py,sha256=J6v5H7Jzvb-g81xp_2QACKwO7LxHQA6ikryMU7zXwN8,5273 -packaging/markers.py,sha256=dWKSqn5Sp-jDmOG-W3GfLHKjwhf1IsznbT71VlBoB5M,10671 -packaging/metadata.py,sha256=KINuSkJ12u-SyoKNTy_pHNGAfMUtxNvZ53qA1zAKcKI,32349 -packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -packaging/requirements.py,sha256=gYyRSAdbrIyKDY66ugIDUQjRMvxkH2ALioTmX3tnL6o,2947 -packaging/specifiers.py,sha256=rjpc3hoJuunRIT6DdH7gLTnQ5j5QKSuWjoTC5sdHtHI,39714 -packaging/tags.py,sha256=y8EbheOu9WS7s-MebaXMcHMF-jzsA_C1Lz5XRTiSy4w,18883 -packaging/utils.py,sha256=NAdYUwnlAOpkat_RthavX8a07YuVxgGL_vwrx73GSDM,5287 -packaging/version.py,sha256=V0H3SOj_8werrvorrb6QDLRhlcqSErNTTkCvvfszhDI,16198 diff --git a/setuptools/_vendor/packaging-24.2.dist-info/INSTALLER b/setuptools/_vendor/packaging-24.2.dist-info/INSTALLER new file mode 100644 index 0000000000..5c69047b2e --- /dev/null +++ b/setuptools/_vendor/packaging-24.2.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/setuptools/_vendor/packaging-24.1.dist-info/LICENSE b/setuptools/_vendor/packaging-24.2.dist-info/LICENSE similarity index 100% rename from setuptools/_vendor/packaging-24.1.dist-info/LICENSE rename to setuptools/_vendor/packaging-24.2.dist-info/LICENSE diff --git a/setuptools/_vendor/packaging-24.1.dist-info/LICENSE.APACHE b/setuptools/_vendor/packaging-24.2.dist-info/LICENSE.APACHE similarity index 100% rename from setuptools/_vendor/packaging-24.1.dist-info/LICENSE.APACHE rename to setuptools/_vendor/packaging-24.2.dist-info/LICENSE.APACHE diff --git a/setuptools/_vendor/packaging-24.1.dist-info/LICENSE.BSD b/setuptools/_vendor/packaging-24.2.dist-info/LICENSE.BSD similarity index 100% rename from setuptools/_vendor/packaging-24.1.dist-info/LICENSE.BSD rename to setuptools/_vendor/packaging-24.2.dist-info/LICENSE.BSD diff --git a/setuptools/_vendor/packaging-24.1.dist-info/METADATA b/setuptools/_vendor/packaging-24.2.dist-info/METADATA similarity index 98% rename from setuptools/_vendor/packaging-24.1.dist-info/METADATA rename to setuptools/_vendor/packaging-24.2.dist-info/METADATA index 255dc46e0e..1479c8694b 100644 --- a/setuptools/_vendor/packaging-24.1.dist-info/METADATA +++ b/setuptools/_vendor/packaging-24.2.dist-info/METADATA @@ -1,6 +1,6 @@ -Metadata-Version: 2.1 +Metadata-Version: 2.3 Name: packaging -Version: 24.1 +Version: 24.2 Summary: Core utilities for Python packages Author-email: Donald Stufft Requires-Python: >=3.8 diff --git a/setuptools/_vendor/packaging-24.2.dist-info/RECORD b/setuptools/_vendor/packaging-24.2.dist-info/RECORD new file mode 100644 index 0000000000..678aa5a501 --- /dev/null +++ b/setuptools/_vendor/packaging-24.2.dist-info/RECORD @@ -0,0 +1,25 @@ +packaging-24.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +packaging-24.2.dist-info/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197 +packaging-24.2.dist-info/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174 +packaging-24.2.dist-info/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344 +packaging-24.2.dist-info/METADATA,sha256=ohH86s6k5mIfQxY2TS0LcSfADeOFa4BiCC-bxZV-pNs,3204 +packaging-24.2.dist-info/RECORD,, +packaging-24.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +packaging-24.2.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82 +packaging/__init__.py,sha256=dk4Ta_vmdVJxYHDcfyhvQNw8V3PgSBomKNXqg-D2JDY,494 +packaging/_elffile.py,sha256=cflAQAkE25tzhYmq_aCi72QfbT_tn891tPzfpbeHOwE,3306 +packaging/_manylinux.py,sha256=vl5OCoz4kx80H5rwXKeXWjl9WNISGmr4ZgTpTP9lU9c,9612 +packaging/_musllinux.py,sha256=p9ZqNYiOItGee8KcZFeHF_YcdhVwGHdK6r-8lgixvGQ,2694 +packaging/_parser.py,sha256=s_TvTvDNK0NrM2QB3VKThdWFM4Nc0P6JnkObkl3MjpM,10236 +packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431 +packaging/_tokenizer.py,sha256=J6v5H7Jzvb-g81xp_2QACKwO7LxHQA6ikryMU7zXwN8,5273 +packaging/licenses/__init__.py,sha256=1x5M1nEYjcgwEbLt0dXwz2ukjr18DiCzC0sraQqJ-Ww,5715 +packaging/licenses/_spdx.py,sha256=oAm1ztPFwlsmCKe7lAAsv_OIOfS1cWDu9bNBkeu-2ns,48398 +packaging/markers.py,sha256=c89TNzB7ZdGYhkovm6PYmqGyHxXlYVaLW591PHUNKD8,10561 +packaging/metadata.py,sha256=YJibM7GYe4re8-0a3OlXmGS-XDgTEoO4tlBt2q25Bng,34762 +packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +packaging/requirements.py,sha256=gYyRSAdbrIyKDY66ugIDUQjRMvxkH2ALioTmX3tnL6o,2947 +packaging/specifiers.py,sha256=GG1wPNMcL0fMJO68vF53wKMdwnfehDcaI-r9NpTfilA,40074 +packaging/tags.py,sha256=CFqrJzAzc2XNGexerH__T-Y5Iwq7WbsYXsiLERLWxY0,21014 +packaging/utils.py,sha256=0F3Hh9OFuRgrhTgGZUl5K22Fv1YP2tZl1z_2gO6kJiA,5050 +packaging/version.py,sha256=olfyuk_DPbflNkJ4wBWetXQ17c74x3DB501degUv7DY,16676 diff --git a/setuptools/_vendor/packaging-24.1.dist-info/REQUESTED b/setuptools/_vendor/packaging-24.2.dist-info/REQUESTED similarity index 100% rename from setuptools/_vendor/packaging-24.1.dist-info/REQUESTED rename to setuptools/_vendor/packaging-24.2.dist-info/REQUESTED diff --git a/setuptools/_vendor/packaging-24.1.dist-info/WHEEL b/setuptools/_vendor/packaging-24.2.dist-info/WHEEL similarity index 71% rename from setuptools/_vendor/packaging-24.1.dist-info/WHEEL rename to setuptools/_vendor/packaging-24.2.dist-info/WHEEL index 3b5e64b5e6..e3c6feefa2 100644 --- a/setuptools/_vendor/packaging-24.1.dist-info/WHEEL +++ b/setuptools/_vendor/packaging-24.2.dist-info/WHEEL @@ -1,4 +1,4 @@ Wheel-Version: 1.0 -Generator: flit 3.9.0 +Generator: flit 3.10.1 Root-Is-Purelib: true Tag: py3-none-any diff --git a/setuptools/_vendor/packaging/__init__.py b/setuptools/_vendor/packaging/__init__.py index 9ba41d8357..d79f73c574 100644 --- a/setuptools/_vendor/packaging/__init__.py +++ b/setuptools/_vendor/packaging/__init__.py @@ -6,10 +6,10 @@ __summary__ = "Core utilities for Python packages" __uri__ = "https://github.com/pypa/packaging" -__version__ = "24.1" +__version__ = "24.2" __author__ = "Donald Stufft and individual contributors" __email__ = "donald@stufft.io" __license__ = "BSD-2-Clause or Apache-2.0" -__copyright__ = "2014 %s" % __author__ +__copyright__ = f"2014 {__author__}" diff --git a/setuptools/_vendor/packaging/_elffile.py b/setuptools/_vendor/packaging/_elffile.py index f7a02180bf..25f4282cc2 100644 --- a/setuptools/_vendor/packaging/_elffile.py +++ b/setuptools/_vendor/packaging/_elffile.py @@ -48,8 +48,8 @@ def __init__(self, f: IO[bytes]) -> None: try: ident = self._read("16B") - except struct.error: - raise ELFInvalid("unable to parse identification") + except struct.error as e: + raise ELFInvalid("unable to parse identification") from e magic = bytes(ident[:4]) if magic != b"\x7fELF": raise ELFInvalid(f"invalid magic: {magic!r}") @@ -67,11 +67,11 @@ def __init__(self, f: IO[bytes]) -> None: (2, 1): ("HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB. }[(self.capacity, self.encoding)] - except KeyError: + except KeyError as e: raise ELFInvalid( f"unrecognized capacity ({self.capacity}) or " f"encoding ({self.encoding})" - ) + ) from e try: ( diff --git a/setuptools/_vendor/packaging/_manylinux.py b/setuptools/_vendor/packaging/_manylinux.py index 08f651fbd8..61339a6fcc 100644 --- a/setuptools/_vendor/packaging/_manylinux.py +++ b/setuptools/_vendor/packaging/_manylinux.py @@ -164,6 +164,7 @@ def _parse_glibc_version(version_str: str) -> tuple[int, int]: f"Expected glibc version with 2 components major.minor," f" got: {version_str}", RuntimeWarning, + stacklevel=2, ) return -1, -1 return int(m.group("major")), int(m.group("minor")) diff --git a/setuptools/_vendor/packaging/licenses/__init__.py b/setuptools/_vendor/packaging/licenses/__init__.py new file mode 100644 index 0000000000..569156d6ca --- /dev/null +++ b/setuptools/_vendor/packaging/licenses/__init__.py @@ -0,0 +1,145 @@ +####################################################################################### +# +# Adapted from: +# https://github.com/pypa/hatch/blob/5352e44/backend/src/hatchling/licenses/parse.py +# +# MIT License +# +# Copyright (c) 2017-present Ofek Lev +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be included in all copies +# or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# +# With additional allowance of arbitrary `LicenseRef-` identifiers, not just +# `LicenseRef-Public-Domain` and `LicenseRef-Proprietary`. +# +####################################################################################### +from __future__ import annotations + +import re +from typing import NewType, cast + +from packaging.licenses._spdx import EXCEPTIONS, LICENSES + +__all__ = [ + "NormalizedLicenseExpression", + "InvalidLicenseExpression", + "canonicalize_license_expression", +] + +license_ref_allowed = re.compile("^[A-Za-z0-9.-]*$") + +NormalizedLicenseExpression = NewType("NormalizedLicenseExpression", str) + + +class InvalidLicenseExpression(ValueError): + """Raised when a license-expression string is invalid + + >>> canonicalize_license_expression("invalid") + Traceback (most recent call last): + ... + packaging.licenses.InvalidLicenseExpression: Invalid license expression: 'invalid' + """ + + +def canonicalize_license_expression( + raw_license_expression: str, +) -> NormalizedLicenseExpression: + if not raw_license_expression: + message = f"Invalid license expression: {raw_license_expression!r}" + raise InvalidLicenseExpression(message) + + # Pad any parentheses so tokenization can be achieved by merely splitting on + # whitespace. + license_expression = raw_license_expression.replace("(", " ( ").replace(")", " ) ") + licenseref_prefix = "LicenseRef-" + license_refs = { + ref.lower(): "LicenseRef-" + ref[len(licenseref_prefix) :] + for ref in license_expression.split() + if ref.lower().startswith(licenseref_prefix.lower()) + } + + # Normalize to lower case so we can look up licenses/exceptions + # and so boolean operators are Python-compatible. + license_expression = license_expression.lower() + + tokens = license_expression.split() + + # Rather than implementing boolean logic, we create an expression that Python can + # parse. Everything that is not involved with the grammar itself is treated as + # `False` and the expression should evaluate as such. + python_tokens = [] + for token in tokens: + if token not in {"or", "and", "with", "(", ")"}: + python_tokens.append("False") + elif token == "with": + python_tokens.append("or") + elif token == "(" and python_tokens and python_tokens[-1] not in {"or", "and"}: + message = f"Invalid license expression: {raw_license_expression!r}" + raise InvalidLicenseExpression(message) + else: + python_tokens.append(token) + + python_expression = " ".join(python_tokens) + try: + invalid = eval(python_expression, globals(), locals()) + except Exception: + invalid = True + + if invalid is not False: + message = f"Invalid license expression: {raw_license_expression!r}" + raise InvalidLicenseExpression(message) from None + + # Take a final pass to check for unknown licenses/exceptions. + normalized_tokens = [] + for token in tokens: + if token in {"or", "and", "with", "(", ")"}: + normalized_tokens.append(token.upper()) + continue + + if normalized_tokens and normalized_tokens[-1] == "WITH": + if token not in EXCEPTIONS: + message = f"Unknown license exception: {token!r}" + raise InvalidLicenseExpression(message) + + normalized_tokens.append(EXCEPTIONS[token]["id"]) + else: + if token.endswith("+"): + final_token = token[:-1] + suffix = "+" + else: + final_token = token + suffix = "" + + if final_token.startswith("licenseref-"): + if not license_ref_allowed.match(final_token): + message = f"Invalid licenseref: {final_token!r}" + raise InvalidLicenseExpression(message) + normalized_tokens.append(license_refs[final_token] + suffix) + else: + if final_token not in LICENSES: + message = f"Unknown license: {final_token!r}" + raise InvalidLicenseExpression(message) + normalized_tokens.append(LICENSES[final_token]["id"] + suffix) + + normalized_expression = " ".join(normalized_tokens) + + return cast( + NormalizedLicenseExpression, + normalized_expression.replace("( ", "(").replace(" )", ")"), + ) diff --git a/setuptools/_vendor/packaging/licenses/_spdx.py b/setuptools/_vendor/packaging/licenses/_spdx.py new file mode 100644 index 0000000000..eac22276a3 --- /dev/null +++ b/setuptools/_vendor/packaging/licenses/_spdx.py @@ -0,0 +1,759 @@ + +from __future__ import annotations + +from typing import TypedDict + +class SPDXLicense(TypedDict): + id: str + deprecated: bool + +class SPDXException(TypedDict): + id: str + deprecated: bool + + +VERSION = '3.25.0' + +LICENSES: dict[str, SPDXLicense] = { + '0bsd': {'id': '0BSD', 'deprecated': False}, + '3d-slicer-1.0': {'id': '3D-Slicer-1.0', 'deprecated': False}, + 'aal': {'id': 'AAL', 'deprecated': False}, + 'abstyles': {'id': 'Abstyles', 'deprecated': False}, + 'adacore-doc': {'id': 'AdaCore-doc', 'deprecated': False}, + 'adobe-2006': {'id': 'Adobe-2006', 'deprecated': False}, + 'adobe-display-postscript': {'id': 'Adobe-Display-PostScript', 'deprecated': False}, + 'adobe-glyph': {'id': 'Adobe-Glyph', 'deprecated': False}, + 'adobe-utopia': {'id': 'Adobe-Utopia', 'deprecated': False}, + 'adsl': {'id': 'ADSL', 'deprecated': False}, + 'afl-1.1': {'id': 'AFL-1.1', 'deprecated': False}, + 'afl-1.2': {'id': 'AFL-1.2', 'deprecated': False}, + 'afl-2.0': {'id': 'AFL-2.0', 'deprecated': False}, + 'afl-2.1': {'id': 'AFL-2.1', 'deprecated': False}, + 'afl-3.0': {'id': 'AFL-3.0', 'deprecated': False}, + 'afmparse': {'id': 'Afmparse', 'deprecated': False}, + 'agpl-1.0': {'id': 'AGPL-1.0', 'deprecated': True}, + 'agpl-1.0-only': {'id': 'AGPL-1.0-only', 'deprecated': False}, + 'agpl-1.0-or-later': {'id': 'AGPL-1.0-or-later', 'deprecated': False}, + 'agpl-3.0': {'id': 'AGPL-3.0', 'deprecated': True}, + 'agpl-3.0-only': {'id': 'AGPL-3.0-only', 'deprecated': False}, + 'agpl-3.0-or-later': {'id': 'AGPL-3.0-or-later', 'deprecated': False}, + 'aladdin': {'id': 'Aladdin', 'deprecated': False}, + 'amd-newlib': {'id': 'AMD-newlib', 'deprecated': False}, + 'amdplpa': {'id': 'AMDPLPA', 'deprecated': False}, + 'aml': {'id': 'AML', 'deprecated': False}, + 'aml-glslang': {'id': 'AML-glslang', 'deprecated': False}, + 'ampas': {'id': 'AMPAS', 'deprecated': False}, + 'antlr-pd': {'id': 'ANTLR-PD', 'deprecated': False}, + 'antlr-pd-fallback': {'id': 'ANTLR-PD-fallback', 'deprecated': False}, + 'any-osi': {'id': 'any-OSI', 'deprecated': False}, + 'apache-1.0': {'id': 'Apache-1.0', 'deprecated': False}, + 'apache-1.1': {'id': 'Apache-1.1', 'deprecated': False}, + 'apache-2.0': {'id': 'Apache-2.0', 'deprecated': False}, + 'apafml': {'id': 'APAFML', 'deprecated': False}, + 'apl-1.0': {'id': 'APL-1.0', 'deprecated': False}, + 'app-s2p': {'id': 'App-s2p', 'deprecated': False}, + 'apsl-1.0': {'id': 'APSL-1.0', 'deprecated': False}, + 'apsl-1.1': {'id': 'APSL-1.1', 'deprecated': False}, + 'apsl-1.2': {'id': 'APSL-1.2', 'deprecated': False}, + 'apsl-2.0': {'id': 'APSL-2.0', 'deprecated': False}, + 'arphic-1999': {'id': 'Arphic-1999', 'deprecated': False}, + 'artistic-1.0': {'id': 'Artistic-1.0', 'deprecated': False}, + 'artistic-1.0-cl8': {'id': 'Artistic-1.0-cl8', 'deprecated': False}, + 'artistic-1.0-perl': {'id': 'Artistic-1.0-Perl', 'deprecated': False}, + 'artistic-2.0': {'id': 'Artistic-2.0', 'deprecated': False}, + 'aswf-digital-assets-1.0': {'id': 'ASWF-Digital-Assets-1.0', 'deprecated': False}, + 'aswf-digital-assets-1.1': {'id': 'ASWF-Digital-Assets-1.1', 'deprecated': False}, + 'baekmuk': {'id': 'Baekmuk', 'deprecated': False}, + 'bahyph': {'id': 'Bahyph', 'deprecated': False}, + 'barr': {'id': 'Barr', 'deprecated': False}, + 'bcrypt-solar-designer': {'id': 'bcrypt-Solar-Designer', 'deprecated': False}, + 'beerware': {'id': 'Beerware', 'deprecated': False}, + 'bitstream-charter': {'id': 'Bitstream-Charter', 'deprecated': False}, + 'bitstream-vera': {'id': 'Bitstream-Vera', 'deprecated': False}, + 'bittorrent-1.0': {'id': 'BitTorrent-1.0', 'deprecated': False}, + 'bittorrent-1.1': {'id': 'BitTorrent-1.1', 'deprecated': False}, + 'blessing': {'id': 'blessing', 'deprecated': False}, + 'blueoak-1.0.0': {'id': 'BlueOak-1.0.0', 'deprecated': False}, + 'boehm-gc': {'id': 'Boehm-GC', 'deprecated': False}, + 'borceux': {'id': 'Borceux', 'deprecated': False}, + 'brian-gladman-2-clause': {'id': 'Brian-Gladman-2-Clause', 'deprecated': False}, + 'brian-gladman-3-clause': {'id': 'Brian-Gladman-3-Clause', 'deprecated': False}, + 'bsd-1-clause': {'id': 'BSD-1-Clause', 'deprecated': False}, + 'bsd-2-clause': {'id': 'BSD-2-Clause', 'deprecated': False}, + 'bsd-2-clause-darwin': {'id': 'BSD-2-Clause-Darwin', 'deprecated': False}, + 'bsd-2-clause-first-lines': {'id': 'BSD-2-Clause-first-lines', 'deprecated': False}, + 'bsd-2-clause-freebsd': {'id': 'BSD-2-Clause-FreeBSD', 'deprecated': True}, + 'bsd-2-clause-netbsd': {'id': 'BSD-2-Clause-NetBSD', 'deprecated': True}, + 'bsd-2-clause-patent': {'id': 'BSD-2-Clause-Patent', 'deprecated': False}, + 'bsd-2-clause-views': {'id': 'BSD-2-Clause-Views', 'deprecated': False}, + 'bsd-3-clause': {'id': 'BSD-3-Clause', 'deprecated': False}, + 'bsd-3-clause-acpica': {'id': 'BSD-3-Clause-acpica', 'deprecated': False}, + 'bsd-3-clause-attribution': {'id': 'BSD-3-Clause-Attribution', 'deprecated': False}, + 'bsd-3-clause-clear': {'id': 'BSD-3-Clause-Clear', 'deprecated': False}, + 'bsd-3-clause-flex': {'id': 'BSD-3-Clause-flex', 'deprecated': False}, + 'bsd-3-clause-hp': {'id': 'BSD-3-Clause-HP', 'deprecated': False}, + 'bsd-3-clause-lbnl': {'id': 'BSD-3-Clause-LBNL', 'deprecated': False}, + 'bsd-3-clause-modification': {'id': 'BSD-3-Clause-Modification', 'deprecated': False}, + 'bsd-3-clause-no-military-license': {'id': 'BSD-3-Clause-No-Military-License', 'deprecated': False}, + 'bsd-3-clause-no-nuclear-license': {'id': 'BSD-3-Clause-No-Nuclear-License', 'deprecated': False}, + 'bsd-3-clause-no-nuclear-license-2014': {'id': 'BSD-3-Clause-No-Nuclear-License-2014', 'deprecated': False}, + 'bsd-3-clause-no-nuclear-warranty': {'id': 'BSD-3-Clause-No-Nuclear-Warranty', 'deprecated': False}, + 'bsd-3-clause-open-mpi': {'id': 'BSD-3-Clause-Open-MPI', 'deprecated': False}, + 'bsd-3-clause-sun': {'id': 'BSD-3-Clause-Sun', 'deprecated': False}, + 'bsd-4-clause': {'id': 'BSD-4-Clause', 'deprecated': False}, + 'bsd-4-clause-shortened': {'id': 'BSD-4-Clause-Shortened', 'deprecated': False}, + 'bsd-4-clause-uc': {'id': 'BSD-4-Clause-UC', 'deprecated': False}, + 'bsd-4.3reno': {'id': 'BSD-4.3RENO', 'deprecated': False}, + 'bsd-4.3tahoe': {'id': 'BSD-4.3TAHOE', 'deprecated': False}, + 'bsd-advertising-acknowledgement': {'id': 'BSD-Advertising-Acknowledgement', 'deprecated': False}, + 'bsd-attribution-hpnd-disclaimer': {'id': 'BSD-Attribution-HPND-disclaimer', 'deprecated': False}, + 'bsd-inferno-nettverk': {'id': 'BSD-Inferno-Nettverk', 'deprecated': False}, + 'bsd-protection': {'id': 'BSD-Protection', 'deprecated': False}, + 'bsd-source-beginning-file': {'id': 'BSD-Source-beginning-file', 'deprecated': False}, + 'bsd-source-code': {'id': 'BSD-Source-Code', 'deprecated': False}, + 'bsd-systemics': {'id': 'BSD-Systemics', 'deprecated': False}, + 'bsd-systemics-w3works': {'id': 'BSD-Systemics-W3Works', 'deprecated': False}, + 'bsl-1.0': {'id': 'BSL-1.0', 'deprecated': False}, + 'busl-1.1': {'id': 'BUSL-1.1', 'deprecated': False}, + 'bzip2-1.0.5': {'id': 'bzip2-1.0.5', 'deprecated': True}, + 'bzip2-1.0.6': {'id': 'bzip2-1.0.6', 'deprecated': False}, + 'c-uda-1.0': {'id': 'C-UDA-1.0', 'deprecated': False}, + 'cal-1.0': {'id': 'CAL-1.0', 'deprecated': False}, + 'cal-1.0-combined-work-exception': {'id': 'CAL-1.0-Combined-Work-Exception', 'deprecated': False}, + 'caldera': {'id': 'Caldera', 'deprecated': False}, + 'caldera-no-preamble': {'id': 'Caldera-no-preamble', 'deprecated': False}, + 'catharon': {'id': 'Catharon', 'deprecated': False}, + 'catosl-1.1': {'id': 'CATOSL-1.1', 'deprecated': False}, + 'cc-by-1.0': {'id': 'CC-BY-1.0', 'deprecated': False}, + 'cc-by-2.0': {'id': 'CC-BY-2.0', 'deprecated': False}, + 'cc-by-2.5': {'id': 'CC-BY-2.5', 'deprecated': False}, + 'cc-by-2.5-au': {'id': 'CC-BY-2.5-AU', 'deprecated': False}, + 'cc-by-3.0': {'id': 'CC-BY-3.0', 'deprecated': False}, + 'cc-by-3.0-at': {'id': 'CC-BY-3.0-AT', 'deprecated': False}, + 'cc-by-3.0-au': {'id': 'CC-BY-3.0-AU', 'deprecated': False}, + 'cc-by-3.0-de': {'id': 'CC-BY-3.0-DE', 'deprecated': False}, + 'cc-by-3.0-igo': {'id': 'CC-BY-3.0-IGO', 'deprecated': False}, + 'cc-by-3.0-nl': {'id': 'CC-BY-3.0-NL', 'deprecated': False}, + 'cc-by-3.0-us': {'id': 'CC-BY-3.0-US', 'deprecated': False}, + 'cc-by-4.0': {'id': 'CC-BY-4.0', 'deprecated': False}, + 'cc-by-nc-1.0': {'id': 'CC-BY-NC-1.0', 'deprecated': False}, + 'cc-by-nc-2.0': {'id': 'CC-BY-NC-2.0', 'deprecated': False}, + 'cc-by-nc-2.5': {'id': 'CC-BY-NC-2.5', 'deprecated': False}, + 'cc-by-nc-3.0': {'id': 'CC-BY-NC-3.0', 'deprecated': False}, + 'cc-by-nc-3.0-de': {'id': 'CC-BY-NC-3.0-DE', 'deprecated': False}, + 'cc-by-nc-4.0': {'id': 'CC-BY-NC-4.0', 'deprecated': False}, + 'cc-by-nc-nd-1.0': {'id': 'CC-BY-NC-ND-1.0', 'deprecated': False}, + 'cc-by-nc-nd-2.0': {'id': 'CC-BY-NC-ND-2.0', 'deprecated': False}, + 'cc-by-nc-nd-2.5': {'id': 'CC-BY-NC-ND-2.5', 'deprecated': False}, + 'cc-by-nc-nd-3.0': {'id': 'CC-BY-NC-ND-3.0', 'deprecated': False}, + 'cc-by-nc-nd-3.0-de': {'id': 'CC-BY-NC-ND-3.0-DE', 'deprecated': False}, + 'cc-by-nc-nd-3.0-igo': {'id': 'CC-BY-NC-ND-3.0-IGO', 'deprecated': False}, + 'cc-by-nc-nd-4.0': {'id': 'CC-BY-NC-ND-4.0', 'deprecated': False}, + 'cc-by-nc-sa-1.0': {'id': 'CC-BY-NC-SA-1.0', 'deprecated': False}, + 'cc-by-nc-sa-2.0': {'id': 'CC-BY-NC-SA-2.0', 'deprecated': False}, + 'cc-by-nc-sa-2.0-de': {'id': 'CC-BY-NC-SA-2.0-DE', 'deprecated': False}, + 'cc-by-nc-sa-2.0-fr': {'id': 'CC-BY-NC-SA-2.0-FR', 'deprecated': False}, + 'cc-by-nc-sa-2.0-uk': {'id': 'CC-BY-NC-SA-2.0-UK', 'deprecated': False}, + 'cc-by-nc-sa-2.5': {'id': 'CC-BY-NC-SA-2.5', 'deprecated': False}, + 'cc-by-nc-sa-3.0': {'id': 'CC-BY-NC-SA-3.0', 'deprecated': False}, + 'cc-by-nc-sa-3.0-de': {'id': 'CC-BY-NC-SA-3.0-DE', 'deprecated': False}, + 'cc-by-nc-sa-3.0-igo': {'id': 'CC-BY-NC-SA-3.0-IGO', 'deprecated': False}, + 'cc-by-nc-sa-4.0': {'id': 'CC-BY-NC-SA-4.0', 'deprecated': False}, + 'cc-by-nd-1.0': {'id': 'CC-BY-ND-1.0', 'deprecated': False}, + 'cc-by-nd-2.0': {'id': 'CC-BY-ND-2.0', 'deprecated': False}, + 'cc-by-nd-2.5': {'id': 'CC-BY-ND-2.5', 'deprecated': False}, + 'cc-by-nd-3.0': {'id': 'CC-BY-ND-3.0', 'deprecated': False}, + 'cc-by-nd-3.0-de': {'id': 'CC-BY-ND-3.0-DE', 'deprecated': False}, + 'cc-by-nd-4.0': {'id': 'CC-BY-ND-4.0', 'deprecated': False}, + 'cc-by-sa-1.0': {'id': 'CC-BY-SA-1.0', 'deprecated': False}, + 'cc-by-sa-2.0': {'id': 'CC-BY-SA-2.0', 'deprecated': False}, + 'cc-by-sa-2.0-uk': {'id': 'CC-BY-SA-2.0-UK', 'deprecated': False}, + 'cc-by-sa-2.1-jp': {'id': 'CC-BY-SA-2.1-JP', 'deprecated': False}, + 'cc-by-sa-2.5': {'id': 'CC-BY-SA-2.5', 'deprecated': False}, + 'cc-by-sa-3.0': {'id': 'CC-BY-SA-3.0', 'deprecated': False}, + 'cc-by-sa-3.0-at': {'id': 'CC-BY-SA-3.0-AT', 'deprecated': False}, + 'cc-by-sa-3.0-de': {'id': 'CC-BY-SA-3.0-DE', 'deprecated': False}, + 'cc-by-sa-3.0-igo': {'id': 'CC-BY-SA-3.0-IGO', 'deprecated': False}, + 'cc-by-sa-4.0': {'id': 'CC-BY-SA-4.0', 'deprecated': False}, + 'cc-pddc': {'id': 'CC-PDDC', 'deprecated': False}, + 'cc0-1.0': {'id': 'CC0-1.0', 'deprecated': False}, + 'cddl-1.0': {'id': 'CDDL-1.0', 'deprecated': False}, + 'cddl-1.1': {'id': 'CDDL-1.1', 'deprecated': False}, + 'cdl-1.0': {'id': 'CDL-1.0', 'deprecated': False}, + 'cdla-permissive-1.0': {'id': 'CDLA-Permissive-1.0', 'deprecated': False}, + 'cdla-permissive-2.0': {'id': 'CDLA-Permissive-2.0', 'deprecated': False}, + 'cdla-sharing-1.0': {'id': 'CDLA-Sharing-1.0', 'deprecated': False}, + 'cecill-1.0': {'id': 'CECILL-1.0', 'deprecated': False}, + 'cecill-1.1': {'id': 'CECILL-1.1', 'deprecated': False}, + 'cecill-2.0': {'id': 'CECILL-2.0', 'deprecated': False}, + 'cecill-2.1': {'id': 'CECILL-2.1', 'deprecated': False}, + 'cecill-b': {'id': 'CECILL-B', 'deprecated': False}, + 'cecill-c': {'id': 'CECILL-C', 'deprecated': False}, + 'cern-ohl-1.1': {'id': 'CERN-OHL-1.1', 'deprecated': False}, + 'cern-ohl-1.2': {'id': 'CERN-OHL-1.2', 'deprecated': False}, + 'cern-ohl-p-2.0': {'id': 'CERN-OHL-P-2.0', 'deprecated': False}, + 'cern-ohl-s-2.0': {'id': 'CERN-OHL-S-2.0', 'deprecated': False}, + 'cern-ohl-w-2.0': {'id': 'CERN-OHL-W-2.0', 'deprecated': False}, + 'cfitsio': {'id': 'CFITSIO', 'deprecated': False}, + 'check-cvs': {'id': 'check-cvs', 'deprecated': False}, + 'checkmk': {'id': 'checkmk', 'deprecated': False}, + 'clartistic': {'id': 'ClArtistic', 'deprecated': False}, + 'clips': {'id': 'Clips', 'deprecated': False}, + 'cmu-mach': {'id': 'CMU-Mach', 'deprecated': False}, + 'cmu-mach-nodoc': {'id': 'CMU-Mach-nodoc', 'deprecated': False}, + 'cnri-jython': {'id': 'CNRI-Jython', 'deprecated': False}, + 'cnri-python': {'id': 'CNRI-Python', 'deprecated': False}, + 'cnri-python-gpl-compatible': {'id': 'CNRI-Python-GPL-Compatible', 'deprecated': False}, + 'coil-1.0': {'id': 'COIL-1.0', 'deprecated': False}, + 'community-spec-1.0': {'id': 'Community-Spec-1.0', 'deprecated': False}, + 'condor-1.1': {'id': 'Condor-1.1', 'deprecated': False}, + 'copyleft-next-0.3.0': {'id': 'copyleft-next-0.3.0', 'deprecated': False}, + 'copyleft-next-0.3.1': {'id': 'copyleft-next-0.3.1', 'deprecated': False}, + 'cornell-lossless-jpeg': {'id': 'Cornell-Lossless-JPEG', 'deprecated': False}, + 'cpal-1.0': {'id': 'CPAL-1.0', 'deprecated': False}, + 'cpl-1.0': {'id': 'CPL-1.0', 'deprecated': False}, + 'cpol-1.02': {'id': 'CPOL-1.02', 'deprecated': False}, + 'cronyx': {'id': 'Cronyx', 'deprecated': False}, + 'crossword': {'id': 'Crossword', 'deprecated': False}, + 'crystalstacker': {'id': 'CrystalStacker', 'deprecated': False}, + 'cua-opl-1.0': {'id': 'CUA-OPL-1.0', 'deprecated': False}, + 'cube': {'id': 'Cube', 'deprecated': False}, + 'curl': {'id': 'curl', 'deprecated': False}, + 'cve-tou': {'id': 'cve-tou', 'deprecated': False}, + 'd-fsl-1.0': {'id': 'D-FSL-1.0', 'deprecated': False}, + 'dec-3-clause': {'id': 'DEC-3-Clause', 'deprecated': False}, + 'diffmark': {'id': 'diffmark', 'deprecated': False}, + 'dl-de-by-2.0': {'id': 'DL-DE-BY-2.0', 'deprecated': False}, + 'dl-de-zero-2.0': {'id': 'DL-DE-ZERO-2.0', 'deprecated': False}, + 'doc': {'id': 'DOC', 'deprecated': False}, + 'docbook-schema': {'id': 'DocBook-Schema', 'deprecated': False}, + 'docbook-xml': {'id': 'DocBook-XML', 'deprecated': False}, + 'dotseqn': {'id': 'Dotseqn', 'deprecated': False}, + 'drl-1.0': {'id': 'DRL-1.0', 'deprecated': False}, + 'drl-1.1': {'id': 'DRL-1.1', 'deprecated': False}, + 'dsdp': {'id': 'DSDP', 'deprecated': False}, + 'dtoa': {'id': 'dtoa', 'deprecated': False}, + 'dvipdfm': {'id': 'dvipdfm', 'deprecated': False}, + 'ecl-1.0': {'id': 'ECL-1.0', 'deprecated': False}, + 'ecl-2.0': {'id': 'ECL-2.0', 'deprecated': False}, + 'ecos-2.0': {'id': 'eCos-2.0', 'deprecated': True}, + 'efl-1.0': {'id': 'EFL-1.0', 'deprecated': False}, + 'efl-2.0': {'id': 'EFL-2.0', 'deprecated': False}, + 'egenix': {'id': 'eGenix', 'deprecated': False}, + 'elastic-2.0': {'id': 'Elastic-2.0', 'deprecated': False}, + 'entessa': {'id': 'Entessa', 'deprecated': False}, + 'epics': {'id': 'EPICS', 'deprecated': False}, + 'epl-1.0': {'id': 'EPL-1.0', 'deprecated': False}, + 'epl-2.0': {'id': 'EPL-2.0', 'deprecated': False}, + 'erlpl-1.1': {'id': 'ErlPL-1.1', 'deprecated': False}, + 'etalab-2.0': {'id': 'etalab-2.0', 'deprecated': False}, + 'eudatagrid': {'id': 'EUDatagrid', 'deprecated': False}, + 'eupl-1.0': {'id': 'EUPL-1.0', 'deprecated': False}, + 'eupl-1.1': {'id': 'EUPL-1.1', 'deprecated': False}, + 'eupl-1.2': {'id': 'EUPL-1.2', 'deprecated': False}, + 'eurosym': {'id': 'Eurosym', 'deprecated': False}, + 'fair': {'id': 'Fair', 'deprecated': False}, + 'fbm': {'id': 'FBM', 'deprecated': False}, + 'fdk-aac': {'id': 'FDK-AAC', 'deprecated': False}, + 'ferguson-twofish': {'id': 'Ferguson-Twofish', 'deprecated': False}, + 'frameworx-1.0': {'id': 'Frameworx-1.0', 'deprecated': False}, + 'freebsd-doc': {'id': 'FreeBSD-DOC', 'deprecated': False}, + 'freeimage': {'id': 'FreeImage', 'deprecated': False}, + 'fsfap': {'id': 'FSFAP', 'deprecated': False}, + 'fsfap-no-warranty-disclaimer': {'id': 'FSFAP-no-warranty-disclaimer', 'deprecated': False}, + 'fsful': {'id': 'FSFUL', 'deprecated': False}, + 'fsfullr': {'id': 'FSFULLR', 'deprecated': False}, + 'fsfullrwd': {'id': 'FSFULLRWD', 'deprecated': False}, + 'ftl': {'id': 'FTL', 'deprecated': False}, + 'furuseth': {'id': 'Furuseth', 'deprecated': False}, + 'fwlw': {'id': 'fwlw', 'deprecated': False}, + 'gcr-docs': {'id': 'GCR-docs', 'deprecated': False}, + 'gd': {'id': 'GD', 'deprecated': False}, + 'gfdl-1.1': {'id': 'GFDL-1.1', 'deprecated': True}, + 'gfdl-1.1-invariants-only': {'id': 'GFDL-1.1-invariants-only', 'deprecated': False}, + 'gfdl-1.1-invariants-or-later': {'id': 'GFDL-1.1-invariants-or-later', 'deprecated': False}, + 'gfdl-1.1-no-invariants-only': {'id': 'GFDL-1.1-no-invariants-only', 'deprecated': False}, + 'gfdl-1.1-no-invariants-or-later': {'id': 'GFDL-1.1-no-invariants-or-later', 'deprecated': False}, + 'gfdl-1.1-only': {'id': 'GFDL-1.1-only', 'deprecated': False}, + 'gfdl-1.1-or-later': {'id': 'GFDL-1.1-or-later', 'deprecated': False}, + 'gfdl-1.2': {'id': 'GFDL-1.2', 'deprecated': True}, + 'gfdl-1.2-invariants-only': {'id': 'GFDL-1.2-invariants-only', 'deprecated': False}, + 'gfdl-1.2-invariants-or-later': {'id': 'GFDL-1.2-invariants-or-later', 'deprecated': False}, + 'gfdl-1.2-no-invariants-only': {'id': 'GFDL-1.2-no-invariants-only', 'deprecated': False}, + 'gfdl-1.2-no-invariants-or-later': {'id': 'GFDL-1.2-no-invariants-or-later', 'deprecated': False}, + 'gfdl-1.2-only': {'id': 'GFDL-1.2-only', 'deprecated': False}, + 'gfdl-1.2-or-later': {'id': 'GFDL-1.2-or-later', 'deprecated': False}, + 'gfdl-1.3': {'id': 'GFDL-1.3', 'deprecated': True}, + 'gfdl-1.3-invariants-only': {'id': 'GFDL-1.3-invariants-only', 'deprecated': False}, + 'gfdl-1.3-invariants-or-later': {'id': 'GFDL-1.3-invariants-or-later', 'deprecated': False}, + 'gfdl-1.3-no-invariants-only': {'id': 'GFDL-1.3-no-invariants-only', 'deprecated': False}, + 'gfdl-1.3-no-invariants-or-later': {'id': 'GFDL-1.3-no-invariants-or-later', 'deprecated': False}, + 'gfdl-1.3-only': {'id': 'GFDL-1.3-only', 'deprecated': False}, + 'gfdl-1.3-or-later': {'id': 'GFDL-1.3-or-later', 'deprecated': False}, + 'giftware': {'id': 'Giftware', 'deprecated': False}, + 'gl2ps': {'id': 'GL2PS', 'deprecated': False}, + 'glide': {'id': 'Glide', 'deprecated': False}, + 'glulxe': {'id': 'Glulxe', 'deprecated': False}, + 'glwtpl': {'id': 'GLWTPL', 'deprecated': False}, + 'gnuplot': {'id': 'gnuplot', 'deprecated': False}, + 'gpl-1.0': {'id': 'GPL-1.0', 'deprecated': True}, + 'gpl-1.0+': {'id': 'GPL-1.0+', 'deprecated': True}, + 'gpl-1.0-only': {'id': 'GPL-1.0-only', 'deprecated': False}, + 'gpl-1.0-or-later': {'id': 'GPL-1.0-or-later', 'deprecated': False}, + 'gpl-2.0': {'id': 'GPL-2.0', 'deprecated': True}, + 'gpl-2.0+': {'id': 'GPL-2.0+', 'deprecated': True}, + 'gpl-2.0-only': {'id': 'GPL-2.0-only', 'deprecated': False}, + 'gpl-2.0-or-later': {'id': 'GPL-2.0-or-later', 'deprecated': False}, + 'gpl-2.0-with-autoconf-exception': {'id': 'GPL-2.0-with-autoconf-exception', 'deprecated': True}, + 'gpl-2.0-with-bison-exception': {'id': 'GPL-2.0-with-bison-exception', 'deprecated': True}, + 'gpl-2.0-with-classpath-exception': {'id': 'GPL-2.0-with-classpath-exception', 'deprecated': True}, + 'gpl-2.0-with-font-exception': {'id': 'GPL-2.0-with-font-exception', 'deprecated': True}, + 'gpl-2.0-with-gcc-exception': {'id': 'GPL-2.0-with-GCC-exception', 'deprecated': True}, + 'gpl-3.0': {'id': 'GPL-3.0', 'deprecated': True}, + 'gpl-3.0+': {'id': 'GPL-3.0+', 'deprecated': True}, + 'gpl-3.0-only': {'id': 'GPL-3.0-only', 'deprecated': False}, + 'gpl-3.0-or-later': {'id': 'GPL-3.0-or-later', 'deprecated': False}, + 'gpl-3.0-with-autoconf-exception': {'id': 'GPL-3.0-with-autoconf-exception', 'deprecated': True}, + 'gpl-3.0-with-gcc-exception': {'id': 'GPL-3.0-with-GCC-exception', 'deprecated': True}, + 'graphics-gems': {'id': 'Graphics-Gems', 'deprecated': False}, + 'gsoap-1.3b': {'id': 'gSOAP-1.3b', 'deprecated': False}, + 'gtkbook': {'id': 'gtkbook', 'deprecated': False}, + 'gutmann': {'id': 'Gutmann', 'deprecated': False}, + 'haskellreport': {'id': 'HaskellReport', 'deprecated': False}, + 'hdparm': {'id': 'hdparm', 'deprecated': False}, + 'hidapi': {'id': 'HIDAPI', 'deprecated': False}, + 'hippocratic-2.1': {'id': 'Hippocratic-2.1', 'deprecated': False}, + 'hp-1986': {'id': 'HP-1986', 'deprecated': False}, + 'hp-1989': {'id': 'HP-1989', 'deprecated': False}, + 'hpnd': {'id': 'HPND', 'deprecated': False}, + 'hpnd-dec': {'id': 'HPND-DEC', 'deprecated': False}, + 'hpnd-doc': {'id': 'HPND-doc', 'deprecated': False}, + 'hpnd-doc-sell': {'id': 'HPND-doc-sell', 'deprecated': False}, + 'hpnd-export-us': {'id': 'HPND-export-US', 'deprecated': False}, + 'hpnd-export-us-acknowledgement': {'id': 'HPND-export-US-acknowledgement', 'deprecated': False}, + 'hpnd-export-us-modify': {'id': 'HPND-export-US-modify', 'deprecated': False}, + 'hpnd-export2-us': {'id': 'HPND-export2-US', 'deprecated': False}, + 'hpnd-fenneberg-livingston': {'id': 'HPND-Fenneberg-Livingston', 'deprecated': False}, + 'hpnd-inria-imag': {'id': 'HPND-INRIA-IMAG', 'deprecated': False}, + 'hpnd-intel': {'id': 'HPND-Intel', 'deprecated': False}, + 'hpnd-kevlin-henney': {'id': 'HPND-Kevlin-Henney', 'deprecated': False}, + 'hpnd-markus-kuhn': {'id': 'HPND-Markus-Kuhn', 'deprecated': False}, + 'hpnd-merchantability-variant': {'id': 'HPND-merchantability-variant', 'deprecated': False}, + 'hpnd-mit-disclaimer': {'id': 'HPND-MIT-disclaimer', 'deprecated': False}, + 'hpnd-netrek': {'id': 'HPND-Netrek', 'deprecated': False}, + 'hpnd-pbmplus': {'id': 'HPND-Pbmplus', 'deprecated': False}, + 'hpnd-sell-mit-disclaimer-xserver': {'id': 'HPND-sell-MIT-disclaimer-xserver', 'deprecated': False}, + 'hpnd-sell-regexpr': {'id': 'HPND-sell-regexpr', 'deprecated': False}, + 'hpnd-sell-variant': {'id': 'HPND-sell-variant', 'deprecated': False}, + 'hpnd-sell-variant-mit-disclaimer': {'id': 'HPND-sell-variant-MIT-disclaimer', 'deprecated': False}, + 'hpnd-sell-variant-mit-disclaimer-rev': {'id': 'HPND-sell-variant-MIT-disclaimer-rev', 'deprecated': False}, + 'hpnd-uc': {'id': 'HPND-UC', 'deprecated': False}, + 'hpnd-uc-export-us': {'id': 'HPND-UC-export-US', 'deprecated': False}, + 'htmltidy': {'id': 'HTMLTIDY', 'deprecated': False}, + 'ibm-pibs': {'id': 'IBM-pibs', 'deprecated': False}, + 'icu': {'id': 'ICU', 'deprecated': False}, + 'iec-code-components-eula': {'id': 'IEC-Code-Components-EULA', 'deprecated': False}, + 'ijg': {'id': 'IJG', 'deprecated': False}, + 'ijg-short': {'id': 'IJG-short', 'deprecated': False}, + 'imagemagick': {'id': 'ImageMagick', 'deprecated': False}, + 'imatix': {'id': 'iMatix', 'deprecated': False}, + 'imlib2': {'id': 'Imlib2', 'deprecated': False}, + 'info-zip': {'id': 'Info-ZIP', 'deprecated': False}, + 'inner-net-2.0': {'id': 'Inner-Net-2.0', 'deprecated': False}, + 'intel': {'id': 'Intel', 'deprecated': False}, + 'intel-acpi': {'id': 'Intel-ACPI', 'deprecated': False}, + 'interbase-1.0': {'id': 'Interbase-1.0', 'deprecated': False}, + 'ipa': {'id': 'IPA', 'deprecated': False}, + 'ipl-1.0': {'id': 'IPL-1.0', 'deprecated': False}, + 'isc': {'id': 'ISC', 'deprecated': False}, + 'isc-veillard': {'id': 'ISC-Veillard', 'deprecated': False}, + 'jam': {'id': 'Jam', 'deprecated': False}, + 'jasper-2.0': {'id': 'JasPer-2.0', 'deprecated': False}, + 'jpl-image': {'id': 'JPL-image', 'deprecated': False}, + 'jpnic': {'id': 'JPNIC', 'deprecated': False}, + 'json': {'id': 'JSON', 'deprecated': False}, + 'kastrup': {'id': 'Kastrup', 'deprecated': False}, + 'kazlib': {'id': 'Kazlib', 'deprecated': False}, + 'knuth-ctan': {'id': 'Knuth-CTAN', 'deprecated': False}, + 'lal-1.2': {'id': 'LAL-1.2', 'deprecated': False}, + 'lal-1.3': {'id': 'LAL-1.3', 'deprecated': False}, + 'latex2e': {'id': 'Latex2e', 'deprecated': False}, + 'latex2e-translated-notice': {'id': 'Latex2e-translated-notice', 'deprecated': False}, + 'leptonica': {'id': 'Leptonica', 'deprecated': False}, + 'lgpl-2.0': {'id': 'LGPL-2.0', 'deprecated': True}, + 'lgpl-2.0+': {'id': 'LGPL-2.0+', 'deprecated': True}, + 'lgpl-2.0-only': {'id': 'LGPL-2.0-only', 'deprecated': False}, + 'lgpl-2.0-or-later': {'id': 'LGPL-2.0-or-later', 'deprecated': False}, + 'lgpl-2.1': {'id': 'LGPL-2.1', 'deprecated': True}, + 'lgpl-2.1+': {'id': 'LGPL-2.1+', 'deprecated': True}, + 'lgpl-2.1-only': {'id': 'LGPL-2.1-only', 'deprecated': False}, + 'lgpl-2.1-or-later': {'id': 'LGPL-2.1-or-later', 'deprecated': False}, + 'lgpl-3.0': {'id': 'LGPL-3.0', 'deprecated': True}, + 'lgpl-3.0+': {'id': 'LGPL-3.0+', 'deprecated': True}, + 'lgpl-3.0-only': {'id': 'LGPL-3.0-only', 'deprecated': False}, + 'lgpl-3.0-or-later': {'id': 'LGPL-3.0-or-later', 'deprecated': False}, + 'lgpllr': {'id': 'LGPLLR', 'deprecated': False}, + 'libpng': {'id': 'Libpng', 'deprecated': False}, + 'libpng-2.0': {'id': 'libpng-2.0', 'deprecated': False}, + 'libselinux-1.0': {'id': 'libselinux-1.0', 'deprecated': False}, + 'libtiff': {'id': 'libtiff', 'deprecated': False}, + 'libutil-david-nugent': {'id': 'libutil-David-Nugent', 'deprecated': False}, + 'liliq-p-1.1': {'id': 'LiLiQ-P-1.1', 'deprecated': False}, + 'liliq-r-1.1': {'id': 'LiLiQ-R-1.1', 'deprecated': False}, + 'liliq-rplus-1.1': {'id': 'LiLiQ-Rplus-1.1', 'deprecated': False}, + 'linux-man-pages-1-para': {'id': 'Linux-man-pages-1-para', 'deprecated': False}, + 'linux-man-pages-copyleft': {'id': 'Linux-man-pages-copyleft', 'deprecated': False}, + 'linux-man-pages-copyleft-2-para': {'id': 'Linux-man-pages-copyleft-2-para', 'deprecated': False}, + 'linux-man-pages-copyleft-var': {'id': 'Linux-man-pages-copyleft-var', 'deprecated': False}, + 'linux-openib': {'id': 'Linux-OpenIB', 'deprecated': False}, + 'loop': {'id': 'LOOP', 'deprecated': False}, + 'lpd-document': {'id': 'LPD-document', 'deprecated': False}, + 'lpl-1.0': {'id': 'LPL-1.0', 'deprecated': False}, + 'lpl-1.02': {'id': 'LPL-1.02', 'deprecated': False}, + 'lppl-1.0': {'id': 'LPPL-1.0', 'deprecated': False}, + 'lppl-1.1': {'id': 'LPPL-1.1', 'deprecated': False}, + 'lppl-1.2': {'id': 'LPPL-1.2', 'deprecated': False}, + 'lppl-1.3a': {'id': 'LPPL-1.3a', 'deprecated': False}, + 'lppl-1.3c': {'id': 'LPPL-1.3c', 'deprecated': False}, + 'lsof': {'id': 'lsof', 'deprecated': False}, + 'lucida-bitmap-fonts': {'id': 'Lucida-Bitmap-Fonts', 'deprecated': False}, + 'lzma-sdk-9.11-to-9.20': {'id': 'LZMA-SDK-9.11-to-9.20', 'deprecated': False}, + 'lzma-sdk-9.22': {'id': 'LZMA-SDK-9.22', 'deprecated': False}, + 'mackerras-3-clause': {'id': 'Mackerras-3-Clause', 'deprecated': False}, + 'mackerras-3-clause-acknowledgment': {'id': 'Mackerras-3-Clause-acknowledgment', 'deprecated': False}, + 'magaz': {'id': 'magaz', 'deprecated': False}, + 'mailprio': {'id': 'mailprio', 'deprecated': False}, + 'makeindex': {'id': 'MakeIndex', 'deprecated': False}, + 'martin-birgmeier': {'id': 'Martin-Birgmeier', 'deprecated': False}, + 'mcphee-slideshow': {'id': 'McPhee-slideshow', 'deprecated': False}, + 'metamail': {'id': 'metamail', 'deprecated': False}, + 'minpack': {'id': 'Minpack', 'deprecated': False}, + 'miros': {'id': 'MirOS', 'deprecated': False}, + 'mit': {'id': 'MIT', 'deprecated': False}, + 'mit-0': {'id': 'MIT-0', 'deprecated': False}, + 'mit-advertising': {'id': 'MIT-advertising', 'deprecated': False}, + 'mit-cmu': {'id': 'MIT-CMU', 'deprecated': False}, + 'mit-enna': {'id': 'MIT-enna', 'deprecated': False}, + 'mit-feh': {'id': 'MIT-feh', 'deprecated': False}, + 'mit-festival': {'id': 'MIT-Festival', 'deprecated': False}, + 'mit-khronos-old': {'id': 'MIT-Khronos-old', 'deprecated': False}, + 'mit-modern-variant': {'id': 'MIT-Modern-Variant', 'deprecated': False}, + 'mit-open-group': {'id': 'MIT-open-group', 'deprecated': False}, + 'mit-testregex': {'id': 'MIT-testregex', 'deprecated': False}, + 'mit-wu': {'id': 'MIT-Wu', 'deprecated': False}, + 'mitnfa': {'id': 'MITNFA', 'deprecated': False}, + 'mmixware': {'id': 'MMIXware', 'deprecated': False}, + 'motosoto': {'id': 'Motosoto', 'deprecated': False}, + 'mpeg-ssg': {'id': 'MPEG-SSG', 'deprecated': False}, + 'mpi-permissive': {'id': 'mpi-permissive', 'deprecated': False}, + 'mpich2': {'id': 'mpich2', 'deprecated': False}, + 'mpl-1.0': {'id': 'MPL-1.0', 'deprecated': False}, + 'mpl-1.1': {'id': 'MPL-1.1', 'deprecated': False}, + 'mpl-2.0': {'id': 'MPL-2.0', 'deprecated': False}, + 'mpl-2.0-no-copyleft-exception': {'id': 'MPL-2.0-no-copyleft-exception', 'deprecated': False}, + 'mplus': {'id': 'mplus', 'deprecated': False}, + 'ms-lpl': {'id': 'MS-LPL', 'deprecated': False}, + 'ms-pl': {'id': 'MS-PL', 'deprecated': False}, + 'ms-rl': {'id': 'MS-RL', 'deprecated': False}, + 'mtll': {'id': 'MTLL', 'deprecated': False}, + 'mulanpsl-1.0': {'id': 'MulanPSL-1.0', 'deprecated': False}, + 'mulanpsl-2.0': {'id': 'MulanPSL-2.0', 'deprecated': False}, + 'multics': {'id': 'Multics', 'deprecated': False}, + 'mup': {'id': 'Mup', 'deprecated': False}, + 'naist-2003': {'id': 'NAIST-2003', 'deprecated': False}, + 'nasa-1.3': {'id': 'NASA-1.3', 'deprecated': False}, + 'naumen': {'id': 'Naumen', 'deprecated': False}, + 'nbpl-1.0': {'id': 'NBPL-1.0', 'deprecated': False}, + 'ncbi-pd': {'id': 'NCBI-PD', 'deprecated': False}, + 'ncgl-uk-2.0': {'id': 'NCGL-UK-2.0', 'deprecated': False}, + 'ncl': {'id': 'NCL', 'deprecated': False}, + 'ncsa': {'id': 'NCSA', 'deprecated': False}, + 'net-snmp': {'id': 'Net-SNMP', 'deprecated': True}, + 'netcdf': {'id': 'NetCDF', 'deprecated': False}, + 'newsletr': {'id': 'Newsletr', 'deprecated': False}, + 'ngpl': {'id': 'NGPL', 'deprecated': False}, + 'nicta-1.0': {'id': 'NICTA-1.0', 'deprecated': False}, + 'nist-pd': {'id': 'NIST-PD', 'deprecated': False}, + 'nist-pd-fallback': {'id': 'NIST-PD-fallback', 'deprecated': False}, + 'nist-software': {'id': 'NIST-Software', 'deprecated': False}, + 'nlod-1.0': {'id': 'NLOD-1.0', 'deprecated': False}, + 'nlod-2.0': {'id': 'NLOD-2.0', 'deprecated': False}, + 'nlpl': {'id': 'NLPL', 'deprecated': False}, + 'nokia': {'id': 'Nokia', 'deprecated': False}, + 'nosl': {'id': 'NOSL', 'deprecated': False}, + 'noweb': {'id': 'Noweb', 'deprecated': False}, + 'npl-1.0': {'id': 'NPL-1.0', 'deprecated': False}, + 'npl-1.1': {'id': 'NPL-1.1', 'deprecated': False}, + 'nposl-3.0': {'id': 'NPOSL-3.0', 'deprecated': False}, + 'nrl': {'id': 'NRL', 'deprecated': False}, + 'ntp': {'id': 'NTP', 'deprecated': False}, + 'ntp-0': {'id': 'NTP-0', 'deprecated': False}, + 'nunit': {'id': 'Nunit', 'deprecated': True}, + 'o-uda-1.0': {'id': 'O-UDA-1.0', 'deprecated': False}, + 'oar': {'id': 'OAR', 'deprecated': False}, + 'occt-pl': {'id': 'OCCT-PL', 'deprecated': False}, + 'oclc-2.0': {'id': 'OCLC-2.0', 'deprecated': False}, + 'odbl-1.0': {'id': 'ODbL-1.0', 'deprecated': False}, + 'odc-by-1.0': {'id': 'ODC-By-1.0', 'deprecated': False}, + 'offis': {'id': 'OFFIS', 'deprecated': False}, + 'ofl-1.0': {'id': 'OFL-1.0', 'deprecated': False}, + 'ofl-1.0-no-rfn': {'id': 'OFL-1.0-no-RFN', 'deprecated': False}, + 'ofl-1.0-rfn': {'id': 'OFL-1.0-RFN', 'deprecated': False}, + 'ofl-1.1': {'id': 'OFL-1.1', 'deprecated': False}, + 'ofl-1.1-no-rfn': {'id': 'OFL-1.1-no-RFN', 'deprecated': False}, + 'ofl-1.1-rfn': {'id': 'OFL-1.1-RFN', 'deprecated': False}, + 'ogc-1.0': {'id': 'OGC-1.0', 'deprecated': False}, + 'ogdl-taiwan-1.0': {'id': 'OGDL-Taiwan-1.0', 'deprecated': False}, + 'ogl-canada-2.0': {'id': 'OGL-Canada-2.0', 'deprecated': False}, + 'ogl-uk-1.0': {'id': 'OGL-UK-1.0', 'deprecated': False}, + 'ogl-uk-2.0': {'id': 'OGL-UK-2.0', 'deprecated': False}, + 'ogl-uk-3.0': {'id': 'OGL-UK-3.0', 'deprecated': False}, + 'ogtsl': {'id': 'OGTSL', 'deprecated': False}, + 'oldap-1.1': {'id': 'OLDAP-1.1', 'deprecated': False}, + 'oldap-1.2': {'id': 'OLDAP-1.2', 'deprecated': False}, + 'oldap-1.3': {'id': 'OLDAP-1.3', 'deprecated': False}, + 'oldap-1.4': {'id': 'OLDAP-1.4', 'deprecated': False}, + 'oldap-2.0': {'id': 'OLDAP-2.0', 'deprecated': False}, + 'oldap-2.0.1': {'id': 'OLDAP-2.0.1', 'deprecated': False}, + 'oldap-2.1': {'id': 'OLDAP-2.1', 'deprecated': False}, + 'oldap-2.2': {'id': 'OLDAP-2.2', 'deprecated': False}, + 'oldap-2.2.1': {'id': 'OLDAP-2.2.1', 'deprecated': False}, + 'oldap-2.2.2': {'id': 'OLDAP-2.2.2', 'deprecated': False}, + 'oldap-2.3': {'id': 'OLDAP-2.3', 'deprecated': False}, + 'oldap-2.4': {'id': 'OLDAP-2.4', 'deprecated': False}, + 'oldap-2.5': {'id': 'OLDAP-2.5', 'deprecated': False}, + 'oldap-2.6': {'id': 'OLDAP-2.6', 'deprecated': False}, + 'oldap-2.7': {'id': 'OLDAP-2.7', 'deprecated': False}, + 'oldap-2.8': {'id': 'OLDAP-2.8', 'deprecated': False}, + 'olfl-1.3': {'id': 'OLFL-1.3', 'deprecated': False}, + 'oml': {'id': 'OML', 'deprecated': False}, + 'openpbs-2.3': {'id': 'OpenPBS-2.3', 'deprecated': False}, + 'openssl': {'id': 'OpenSSL', 'deprecated': False}, + 'openssl-standalone': {'id': 'OpenSSL-standalone', 'deprecated': False}, + 'openvision': {'id': 'OpenVision', 'deprecated': False}, + 'opl-1.0': {'id': 'OPL-1.0', 'deprecated': False}, + 'opl-uk-3.0': {'id': 'OPL-UK-3.0', 'deprecated': False}, + 'opubl-1.0': {'id': 'OPUBL-1.0', 'deprecated': False}, + 'oset-pl-2.1': {'id': 'OSET-PL-2.1', 'deprecated': False}, + 'osl-1.0': {'id': 'OSL-1.0', 'deprecated': False}, + 'osl-1.1': {'id': 'OSL-1.1', 'deprecated': False}, + 'osl-2.0': {'id': 'OSL-2.0', 'deprecated': False}, + 'osl-2.1': {'id': 'OSL-2.1', 'deprecated': False}, + 'osl-3.0': {'id': 'OSL-3.0', 'deprecated': False}, + 'padl': {'id': 'PADL', 'deprecated': False}, + 'parity-6.0.0': {'id': 'Parity-6.0.0', 'deprecated': False}, + 'parity-7.0.0': {'id': 'Parity-7.0.0', 'deprecated': False}, + 'pddl-1.0': {'id': 'PDDL-1.0', 'deprecated': False}, + 'php-3.0': {'id': 'PHP-3.0', 'deprecated': False}, + 'php-3.01': {'id': 'PHP-3.01', 'deprecated': False}, + 'pixar': {'id': 'Pixar', 'deprecated': False}, + 'pkgconf': {'id': 'pkgconf', 'deprecated': False}, + 'plexus': {'id': 'Plexus', 'deprecated': False}, + 'pnmstitch': {'id': 'pnmstitch', 'deprecated': False}, + 'polyform-noncommercial-1.0.0': {'id': 'PolyForm-Noncommercial-1.0.0', 'deprecated': False}, + 'polyform-small-business-1.0.0': {'id': 'PolyForm-Small-Business-1.0.0', 'deprecated': False}, + 'postgresql': {'id': 'PostgreSQL', 'deprecated': False}, + 'ppl': {'id': 'PPL', 'deprecated': False}, + 'psf-2.0': {'id': 'PSF-2.0', 'deprecated': False}, + 'psfrag': {'id': 'psfrag', 'deprecated': False}, + 'psutils': {'id': 'psutils', 'deprecated': False}, + 'python-2.0': {'id': 'Python-2.0', 'deprecated': False}, + 'python-2.0.1': {'id': 'Python-2.0.1', 'deprecated': False}, + 'python-ldap': {'id': 'python-ldap', 'deprecated': False}, + 'qhull': {'id': 'Qhull', 'deprecated': False}, + 'qpl-1.0': {'id': 'QPL-1.0', 'deprecated': False}, + 'qpl-1.0-inria-2004': {'id': 'QPL-1.0-INRIA-2004', 'deprecated': False}, + 'radvd': {'id': 'radvd', 'deprecated': False}, + 'rdisc': {'id': 'Rdisc', 'deprecated': False}, + 'rhecos-1.1': {'id': 'RHeCos-1.1', 'deprecated': False}, + 'rpl-1.1': {'id': 'RPL-1.1', 'deprecated': False}, + 'rpl-1.5': {'id': 'RPL-1.5', 'deprecated': False}, + 'rpsl-1.0': {'id': 'RPSL-1.0', 'deprecated': False}, + 'rsa-md': {'id': 'RSA-MD', 'deprecated': False}, + 'rscpl': {'id': 'RSCPL', 'deprecated': False}, + 'ruby': {'id': 'Ruby', 'deprecated': False}, + 'ruby-pty': {'id': 'Ruby-pty', 'deprecated': False}, + 'sax-pd': {'id': 'SAX-PD', 'deprecated': False}, + 'sax-pd-2.0': {'id': 'SAX-PD-2.0', 'deprecated': False}, + 'saxpath': {'id': 'Saxpath', 'deprecated': False}, + 'scea': {'id': 'SCEA', 'deprecated': False}, + 'schemereport': {'id': 'SchemeReport', 'deprecated': False}, + 'sendmail': {'id': 'Sendmail', 'deprecated': False}, + 'sendmail-8.23': {'id': 'Sendmail-8.23', 'deprecated': False}, + 'sgi-b-1.0': {'id': 'SGI-B-1.0', 'deprecated': False}, + 'sgi-b-1.1': {'id': 'SGI-B-1.1', 'deprecated': False}, + 'sgi-b-2.0': {'id': 'SGI-B-2.0', 'deprecated': False}, + 'sgi-opengl': {'id': 'SGI-OpenGL', 'deprecated': False}, + 'sgp4': {'id': 'SGP4', 'deprecated': False}, + 'shl-0.5': {'id': 'SHL-0.5', 'deprecated': False}, + 'shl-0.51': {'id': 'SHL-0.51', 'deprecated': False}, + 'simpl-2.0': {'id': 'SimPL-2.0', 'deprecated': False}, + 'sissl': {'id': 'SISSL', 'deprecated': False}, + 'sissl-1.2': {'id': 'SISSL-1.2', 'deprecated': False}, + 'sl': {'id': 'SL', 'deprecated': False}, + 'sleepycat': {'id': 'Sleepycat', 'deprecated': False}, + 'smlnj': {'id': 'SMLNJ', 'deprecated': False}, + 'smppl': {'id': 'SMPPL', 'deprecated': False}, + 'snia': {'id': 'SNIA', 'deprecated': False}, + 'snprintf': {'id': 'snprintf', 'deprecated': False}, + 'softsurfer': {'id': 'softSurfer', 'deprecated': False}, + 'soundex': {'id': 'Soundex', 'deprecated': False}, + 'spencer-86': {'id': 'Spencer-86', 'deprecated': False}, + 'spencer-94': {'id': 'Spencer-94', 'deprecated': False}, + 'spencer-99': {'id': 'Spencer-99', 'deprecated': False}, + 'spl-1.0': {'id': 'SPL-1.0', 'deprecated': False}, + 'ssh-keyscan': {'id': 'ssh-keyscan', 'deprecated': False}, + 'ssh-openssh': {'id': 'SSH-OpenSSH', 'deprecated': False}, + 'ssh-short': {'id': 'SSH-short', 'deprecated': False}, + 'ssleay-standalone': {'id': 'SSLeay-standalone', 'deprecated': False}, + 'sspl-1.0': {'id': 'SSPL-1.0', 'deprecated': False}, + 'standardml-nj': {'id': 'StandardML-NJ', 'deprecated': True}, + 'sugarcrm-1.1.3': {'id': 'SugarCRM-1.1.3', 'deprecated': False}, + 'sun-ppp': {'id': 'Sun-PPP', 'deprecated': False}, + 'sun-ppp-2000': {'id': 'Sun-PPP-2000', 'deprecated': False}, + 'sunpro': {'id': 'SunPro', 'deprecated': False}, + 'swl': {'id': 'SWL', 'deprecated': False}, + 'swrule': {'id': 'swrule', 'deprecated': False}, + 'symlinks': {'id': 'Symlinks', 'deprecated': False}, + 'tapr-ohl-1.0': {'id': 'TAPR-OHL-1.0', 'deprecated': False}, + 'tcl': {'id': 'TCL', 'deprecated': False}, + 'tcp-wrappers': {'id': 'TCP-wrappers', 'deprecated': False}, + 'termreadkey': {'id': 'TermReadKey', 'deprecated': False}, + 'tgppl-1.0': {'id': 'TGPPL-1.0', 'deprecated': False}, + 'threeparttable': {'id': 'threeparttable', 'deprecated': False}, + 'tmate': {'id': 'TMate', 'deprecated': False}, + 'torque-1.1': {'id': 'TORQUE-1.1', 'deprecated': False}, + 'tosl': {'id': 'TOSL', 'deprecated': False}, + 'tpdl': {'id': 'TPDL', 'deprecated': False}, + 'tpl-1.0': {'id': 'TPL-1.0', 'deprecated': False}, + 'ttwl': {'id': 'TTWL', 'deprecated': False}, + 'ttyp0': {'id': 'TTYP0', 'deprecated': False}, + 'tu-berlin-1.0': {'id': 'TU-Berlin-1.0', 'deprecated': False}, + 'tu-berlin-2.0': {'id': 'TU-Berlin-2.0', 'deprecated': False}, + 'ubuntu-font-1.0': {'id': 'Ubuntu-font-1.0', 'deprecated': False}, + 'ucar': {'id': 'UCAR', 'deprecated': False}, + 'ucl-1.0': {'id': 'UCL-1.0', 'deprecated': False}, + 'ulem': {'id': 'ulem', 'deprecated': False}, + 'umich-merit': {'id': 'UMich-Merit', 'deprecated': False}, + 'unicode-3.0': {'id': 'Unicode-3.0', 'deprecated': False}, + 'unicode-dfs-2015': {'id': 'Unicode-DFS-2015', 'deprecated': False}, + 'unicode-dfs-2016': {'id': 'Unicode-DFS-2016', 'deprecated': False}, + 'unicode-tou': {'id': 'Unicode-TOU', 'deprecated': False}, + 'unixcrypt': {'id': 'UnixCrypt', 'deprecated': False}, + 'unlicense': {'id': 'Unlicense', 'deprecated': False}, + 'upl-1.0': {'id': 'UPL-1.0', 'deprecated': False}, + 'urt-rle': {'id': 'URT-RLE', 'deprecated': False}, + 'vim': {'id': 'Vim', 'deprecated': False}, + 'vostrom': {'id': 'VOSTROM', 'deprecated': False}, + 'vsl-1.0': {'id': 'VSL-1.0', 'deprecated': False}, + 'w3c': {'id': 'W3C', 'deprecated': False}, + 'w3c-19980720': {'id': 'W3C-19980720', 'deprecated': False}, + 'w3c-20150513': {'id': 'W3C-20150513', 'deprecated': False}, + 'w3m': {'id': 'w3m', 'deprecated': False}, + 'watcom-1.0': {'id': 'Watcom-1.0', 'deprecated': False}, + 'widget-workshop': {'id': 'Widget-Workshop', 'deprecated': False}, + 'wsuipa': {'id': 'Wsuipa', 'deprecated': False}, + 'wtfpl': {'id': 'WTFPL', 'deprecated': False}, + 'wxwindows': {'id': 'wxWindows', 'deprecated': True}, + 'x11': {'id': 'X11', 'deprecated': False}, + 'x11-distribute-modifications-variant': {'id': 'X11-distribute-modifications-variant', 'deprecated': False}, + 'x11-swapped': {'id': 'X11-swapped', 'deprecated': False}, + 'xdebug-1.03': {'id': 'Xdebug-1.03', 'deprecated': False}, + 'xerox': {'id': 'Xerox', 'deprecated': False}, + 'xfig': {'id': 'Xfig', 'deprecated': False}, + 'xfree86-1.1': {'id': 'XFree86-1.1', 'deprecated': False}, + 'xinetd': {'id': 'xinetd', 'deprecated': False}, + 'xkeyboard-config-zinoviev': {'id': 'xkeyboard-config-Zinoviev', 'deprecated': False}, + 'xlock': {'id': 'xlock', 'deprecated': False}, + 'xnet': {'id': 'Xnet', 'deprecated': False}, + 'xpp': {'id': 'xpp', 'deprecated': False}, + 'xskat': {'id': 'XSkat', 'deprecated': False}, + 'xzoom': {'id': 'xzoom', 'deprecated': False}, + 'ypl-1.0': {'id': 'YPL-1.0', 'deprecated': False}, + 'ypl-1.1': {'id': 'YPL-1.1', 'deprecated': False}, + 'zed': {'id': 'Zed', 'deprecated': False}, + 'zeeff': {'id': 'Zeeff', 'deprecated': False}, + 'zend-2.0': {'id': 'Zend-2.0', 'deprecated': False}, + 'zimbra-1.3': {'id': 'Zimbra-1.3', 'deprecated': False}, + 'zimbra-1.4': {'id': 'Zimbra-1.4', 'deprecated': False}, + 'zlib': {'id': 'Zlib', 'deprecated': False}, + 'zlib-acknowledgement': {'id': 'zlib-acknowledgement', 'deprecated': False}, + 'zpl-1.1': {'id': 'ZPL-1.1', 'deprecated': False}, + 'zpl-2.0': {'id': 'ZPL-2.0', 'deprecated': False}, + 'zpl-2.1': {'id': 'ZPL-2.1', 'deprecated': False}, +} + +EXCEPTIONS: dict[str, SPDXException] = { + '389-exception': {'id': '389-exception', 'deprecated': False}, + 'asterisk-exception': {'id': 'Asterisk-exception', 'deprecated': False}, + 'asterisk-linking-protocols-exception': {'id': 'Asterisk-linking-protocols-exception', 'deprecated': False}, + 'autoconf-exception-2.0': {'id': 'Autoconf-exception-2.0', 'deprecated': False}, + 'autoconf-exception-3.0': {'id': 'Autoconf-exception-3.0', 'deprecated': False}, + 'autoconf-exception-generic': {'id': 'Autoconf-exception-generic', 'deprecated': False}, + 'autoconf-exception-generic-3.0': {'id': 'Autoconf-exception-generic-3.0', 'deprecated': False}, + 'autoconf-exception-macro': {'id': 'Autoconf-exception-macro', 'deprecated': False}, + 'bison-exception-1.24': {'id': 'Bison-exception-1.24', 'deprecated': False}, + 'bison-exception-2.2': {'id': 'Bison-exception-2.2', 'deprecated': False}, + 'bootloader-exception': {'id': 'Bootloader-exception', 'deprecated': False}, + 'classpath-exception-2.0': {'id': 'Classpath-exception-2.0', 'deprecated': False}, + 'clisp-exception-2.0': {'id': 'CLISP-exception-2.0', 'deprecated': False}, + 'cryptsetup-openssl-exception': {'id': 'cryptsetup-OpenSSL-exception', 'deprecated': False}, + 'digirule-foss-exception': {'id': 'DigiRule-FOSS-exception', 'deprecated': False}, + 'ecos-exception-2.0': {'id': 'eCos-exception-2.0', 'deprecated': False}, + 'erlang-otp-linking-exception': {'id': 'erlang-otp-linking-exception', 'deprecated': False}, + 'fawkes-runtime-exception': {'id': 'Fawkes-Runtime-exception', 'deprecated': False}, + 'fltk-exception': {'id': 'FLTK-exception', 'deprecated': False}, + 'fmt-exception': {'id': 'fmt-exception', 'deprecated': False}, + 'font-exception-2.0': {'id': 'Font-exception-2.0', 'deprecated': False}, + 'freertos-exception-2.0': {'id': 'freertos-exception-2.0', 'deprecated': False}, + 'gcc-exception-2.0': {'id': 'GCC-exception-2.0', 'deprecated': False}, + 'gcc-exception-2.0-note': {'id': 'GCC-exception-2.0-note', 'deprecated': False}, + 'gcc-exception-3.1': {'id': 'GCC-exception-3.1', 'deprecated': False}, + 'gmsh-exception': {'id': 'Gmsh-exception', 'deprecated': False}, + 'gnat-exception': {'id': 'GNAT-exception', 'deprecated': False}, + 'gnome-examples-exception': {'id': 'GNOME-examples-exception', 'deprecated': False}, + 'gnu-compiler-exception': {'id': 'GNU-compiler-exception', 'deprecated': False}, + 'gnu-javamail-exception': {'id': 'gnu-javamail-exception', 'deprecated': False}, + 'gpl-3.0-interface-exception': {'id': 'GPL-3.0-interface-exception', 'deprecated': False}, + 'gpl-3.0-linking-exception': {'id': 'GPL-3.0-linking-exception', 'deprecated': False}, + 'gpl-3.0-linking-source-exception': {'id': 'GPL-3.0-linking-source-exception', 'deprecated': False}, + 'gpl-cc-1.0': {'id': 'GPL-CC-1.0', 'deprecated': False}, + 'gstreamer-exception-2005': {'id': 'GStreamer-exception-2005', 'deprecated': False}, + 'gstreamer-exception-2008': {'id': 'GStreamer-exception-2008', 'deprecated': False}, + 'i2p-gpl-java-exception': {'id': 'i2p-gpl-java-exception', 'deprecated': False}, + 'kicad-libraries-exception': {'id': 'KiCad-libraries-exception', 'deprecated': False}, + 'lgpl-3.0-linking-exception': {'id': 'LGPL-3.0-linking-exception', 'deprecated': False}, + 'libpri-openh323-exception': {'id': 'libpri-OpenH323-exception', 'deprecated': False}, + 'libtool-exception': {'id': 'Libtool-exception', 'deprecated': False}, + 'linux-syscall-note': {'id': 'Linux-syscall-note', 'deprecated': False}, + 'llgpl': {'id': 'LLGPL', 'deprecated': False}, + 'llvm-exception': {'id': 'LLVM-exception', 'deprecated': False}, + 'lzma-exception': {'id': 'LZMA-exception', 'deprecated': False}, + 'mif-exception': {'id': 'mif-exception', 'deprecated': False}, + 'nokia-qt-exception-1.1': {'id': 'Nokia-Qt-exception-1.1', 'deprecated': True}, + 'ocaml-lgpl-linking-exception': {'id': 'OCaml-LGPL-linking-exception', 'deprecated': False}, + 'occt-exception-1.0': {'id': 'OCCT-exception-1.0', 'deprecated': False}, + 'openjdk-assembly-exception-1.0': {'id': 'OpenJDK-assembly-exception-1.0', 'deprecated': False}, + 'openvpn-openssl-exception': {'id': 'openvpn-openssl-exception', 'deprecated': False}, + 'pcre2-exception': {'id': 'PCRE2-exception', 'deprecated': False}, + 'ps-or-pdf-font-exception-20170817': {'id': 'PS-or-PDF-font-exception-20170817', 'deprecated': False}, + 'qpl-1.0-inria-2004-exception': {'id': 'QPL-1.0-INRIA-2004-exception', 'deprecated': False}, + 'qt-gpl-exception-1.0': {'id': 'Qt-GPL-exception-1.0', 'deprecated': False}, + 'qt-lgpl-exception-1.1': {'id': 'Qt-LGPL-exception-1.1', 'deprecated': False}, + 'qwt-exception-1.0': {'id': 'Qwt-exception-1.0', 'deprecated': False}, + 'romic-exception': {'id': 'romic-exception', 'deprecated': False}, + 'rrdtool-floss-exception-2.0': {'id': 'RRDtool-FLOSS-exception-2.0', 'deprecated': False}, + 'sane-exception': {'id': 'SANE-exception', 'deprecated': False}, + 'shl-2.0': {'id': 'SHL-2.0', 'deprecated': False}, + 'shl-2.1': {'id': 'SHL-2.1', 'deprecated': False}, + 'stunnel-exception': {'id': 'stunnel-exception', 'deprecated': False}, + 'swi-exception': {'id': 'SWI-exception', 'deprecated': False}, + 'swift-exception': {'id': 'Swift-exception', 'deprecated': False}, + 'texinfo-exception': {'id': 'Texinfo-exception', 'deprecated': False}, + 'u-boot-exception-2.0': {'id': 'u-boot-exception-2.0', 'deprecated': False}, + 'ubdl-exception': {'id': 'UBDL-exception', 'deprecated': False}, + 'universal-foss-exception-1.0': {'id': 'Universal-FOSS-exception-1.0', 'deprecated': False}, + 'vsftpd-openssl-exception': {'id': 'vsftpd-openssl-exception', 'deprecated': False}, + 'wxwindows-exception-3.1': {'id': 'WxWindows-exception-3.1', 'deprecated': False}, + 'x11vnc-openssl-exception': {'id': 'x11vnc-openssl-exception', 'deprecated': False}, +} diff --git a/setuptools/_vendor/packaging/markers.py b/setuptools/_vendor/packaging/markers.py index 7ac7bb69a5..fb7f49cf8c 100644 --- a/setuptools/_vendor/packaging/markers.py +++ b/setuptools/_vendor/packaging/markers.py @@ -18,9 +18,9 @@ __all__ = [ "InvalidMarker", + "Marker", "UndefinedComparison", "UndefinedEnvironmentName", - "Marker", "default_environment", ] @@ -232,7 +232,7 @@ def _evaluate_markers(markers: MarkerList, environment: dict[str, str]) -> bool: def format_full_version(info: sys._version_info) -> str: - version = "{0.major}.{0.minor}.{0.micro}".format(info) + version = f"{info.major}.{info.minor}.{info.micro}" kind = info.releaselevel if kind != "final": version += kind[0] + str(info.serial) @@ -309,12 +309,6 @@ def evaluate(self, environment: dict[str, str] | None = None) -> bool: """ current_environment = cast("dict[str, str]", default_environment()) current_environment["extra"] = "" - # Work around platform.python_version() returning something that is not PEP 440 - # compliant for non-tagged Python builds. We preserve default_environment()'s - # behavior of returning platform.python_version() verbatim, and leave it to the - # caller to provide a syntactically valid version if they want to override it. - if current_environment["python_full_version"].endswith("+"): - current_environment["python_full_version"] += "local" if environment is not None: current_environment.update(environment) # The API used to allow setting extra to None. We need to handle this @@ -322,4 +316,16 @@ def evaluate(self, environment: dict[str, str] | None = None) -> bool: if current_environment["extra"] is None: current_environment["extra"] = "" - return _evaluate_markers(self._markers, current_environment) + return _evaluate_markers( + self._markers, _repair_python_full_version(current_environment) + ) + + +def _repair_python_full_version(env: dict[str, str]) -> dict[str, str]: + """ + Work around platform.python_version() returning something that is not PEP 440 + compliant for non-tagged Python builds. + """ + if env["python_full_version"].endswith("+"): + env["python_full_version"] += "local" + return env diff --git a/setuptools/_vendor/packaging/metadata.py b/setuptools/_vendor/packaging/metadata.py index eb8dc844d2..721f411cfc 100644 --- a/setuptools/_vendor/packaging/metadata.py +++ b/setuptools/_vendor/packaging/metadata.py @@ -5,6 +5,8 @@ import email.message import email.parser import email.policy +import pathlib +import sys import typing from typing import ( Any, @@ -15,15 +17,16 @@ cast, ) -from . import requirements, specifiers, utils +from . import licenses, requirements, specifiers, utils from . import version as version_module +from .licenses import NormalizedLicenseExpression T = typing.TypeVar("T") -try: - ExceptionGroup -except NameError: # pragma: no cover +if sys.version_info >= (3, 11): # pragma: no cover + ExceptionGroup = ExceptionGroup +else: # pragma: no cover class ExceptionGroup(Exception): """A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11. @@ -42,9 +45,6 @@ def __init__(self, message: str, exceptions: list[Exception]) -> None: def __repr__(self) -> str: return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})" -else: # pragma: no cover - ExceptionGroup = ExceptionGroup - class InvalidMetadata(ValueError): """A metadata field contains invalid data.""" @@ -128,6 +128,10 @@ class RawMetadata(TypedDict, total=False): # No new fields were added in PEP 685, just some edge case were # tightened up to provide better interoptability. + # Metadata 2.4 - PEP 639 + license_expression: str + license_files: list[str] + _STRING_FIELDS = { "author", @@ -137,6 +141,7 @@ class RawMetadata(TypedDict, total=False): "download_url", "home_page", "license", + "license_expression", "maintainer", "maintainer_email", "metadata_version", @@ -149,6 +154,7 @@ class RawMetadata(TypedDict, total=False): _LIST_FIELDS = { "classifiers", "dynamic", + "license_files", "obsoletes", "obsoletes_dist", "platforms", @@ -167,7 +173,7 @@ class RawMetadata(TypedDict, total=False): def _parse_keywords(data: str) -> list[str]: - """Split a string of comma-separate keyboards into a list of keywords.""" + """Split a string of comma-separated keywords into a list of keywords.""" return [k.strip() for k in data.split(",")] @@ -216,16 +222,18 @@ def _get_payload(msg: email.message.Message, source: bytes | str) -> str: # If our source is a str, then our caller has managed encodings for us, # and we don't need to deal with it. if isinstance(source, str): - payload: str = msg.get_payload() + payload = msg.get_payload() + assert isinstance(payload, str) return payload # If our source is a bytes, then we're managing the encoding and we need # to deal with it. else: - bpayload: bytes = msg.get_payload(decode=True) + bpayload = msg.get_payload(decode=True) + assert isinstance(bpayload, bytes) try: return bpayload.decode("utf8", "strict") - except UnicodeDecodeError: - raise ValueError("payload in an invalid encoding") + except UnicodeDecodeError as exc: + raise ValueError("payload in an invalid encoding") from exc # The various parse_FORMAT functions here are intended to be as lenient as @@ -251,6 +259,8 @@ def _get_payload(msg: email.message.Message, source: bytes | str) -> str: "home-page": "home_page", "keywords": "keywords", "license": "license", + "license-expression": "license_expression", + "license-file": "license_files", "maintainer": "maintainer", "maintainer-email": "maintainer_email", "metadata-version": "metadata_version", @@ -426,7 +436,7 @@ def parse_email(data: bytes | str) -> tuple[RawMetadata, dict[str, list[str]]]: payload = _get_payload(parsed, data) except ValueError: unparsed.setdefault("description", []).append( - parsed.get_payload(decode=isinstance(data, bytes)) + parsed.get_payload(decode=isinstance(data, bytes)) # type: ignore[call-overload] ) else: if payload: @@ -453,8 +463,8 @@ def parse_email(data: bytes | str) -> tuple[RawMetadata, dict[str, list[str]]]: # Keep the two values in sync. -_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"] -_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"] +_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4"] +_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4"] _REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"]) @@ -535,7 +545,7 @@ def _process_name(self, value: str) -> str: except utils.InvalidName as exc: raise self._invalid_metadata( f"{value!r} is invalid for {{field}}", cause=exc - ) + ) from exc else: return value @@ -547,7 +557,7 @@ def _process_version(self, value: str) -> version_module.Version: except version_module.InvalidVersion as exc: raise self._invalid_metadata( f"{value!r} is invalid for {{field}}", cause=exc - ) + ) from exc def _process_summary(self, value: str) -> str: """Check the field contains no newlines.""" @@ -591,10 +601,12 @@ def _process_dynamic(self, value: list[str]) -> list[str]: for dynamic_field in map(str.lower, value): if dynamic_field in {"name", "version", "metadata-version"}: raise self._invalid_metadata( - f"{value!r} is not allowed as a dynamic field" + f"{dynamic_field!r} is not allowed as a dynamic field" ) elif dynamic_field not in _EMAIL_TO_RAW_MAPPING: - raise self._invalid_metadata(f"{value!r} is not a valid dynamic field") + raise self._invalid_metadata( + f"{dynamic_field!r} is not a valid dynamic field" + ) return list(map(str.lower, value)) def _process_provides_extra( @@ -608,7 +620,7 @@ def _process_provides_extra( except utils.InvalidName as exc: raise self._invalid_metadata( f"{name!r} is invalid for {{field}}", cause=exc - ) + ) from exc else: return normalized_names @@ -618,7 +630,7 @@ def _process_requires_python(self, value: str) -> specifiers.SpecifierSet: except specifiers.InvalidSpecifier as exc: raise self._invalid_metadata( f"{value!r} is invalid for {{field}}", cause=exc - ) + ) from exc def _process_requires_dist( self, @@ -629,10 +641,49 @@ def _process_requires_dist( for req in value: reqs.append(requirements.Requirement(req)) except requirements.InvalidRequirement as exc: - raise self._invalid_metadata(f"{req!r} is invalid for {{field}}", cause=exc) + raise self._invalid_metadata( + f"{req!r} is invalid for {{field}}", cause=exc + ) from exc else: return reqs + def _process_license_expression( + self, value: str + ) -> NormalizedLicenseExpression | None: + try: + return licenses.canonicalize_license_expression(value) + except ValueError as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) from exc + + def _process_license_files(self, value: list[str]) -> list[str]: + paths = [] + for path in value: + if ".." in path: + raise self._invalid_metadata( + f"{path!r} is invalid for {{field}}, " + "parent directory indicators are not allowed" + ) + if "*" in path: + raise self._invalid_metadata( + f"{path!r} is invalid for {{field}}, paths must be resolved" + ) + if ( + pathlib.PurePosixPath(path).is_absolute() + or pathlib.PureWindowsPath(path).is_absolute() + ): + raise self._invalid_metadata( + f"{path!r} is invalid for {{field}}, paths must be relative" + ) + if pathlib.PureWindowsPath(path).as_posix() != path: + raise self._invalid_metadata( + f"{path!r} is invalid for {{field}}, " + "paths must use '/' delimiter" + ) + paths.append(path) + return paths + class Metadata: """Representation of distribution metadata. @@ -688,8 +739,8 @@ def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> Metadata: field = _RAW_TO_EMAIL_MAPPING[key] exc = InvalidMetadata( field, - "{field} introduced in metadata version " - "{field_metadata_version}, not {metadata_version}", + f"{field} introduced in metadata version " + f"{field_metadata_version}, not {metadata_version}", ) exceptions.append(exc) continue @@ -733,6 +784,8 @@ def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata: metadata_version: _Validator[_MetadataVersion] = _Validator() """:external:ref:`core-metadata-metadata-version` (required; validated to be a valid metadata version)""" + # `name` is not normalized/typed to NormalizedName so as to provide access to + # the original/raw name. name: _Validator[str] = _Validator() """:external:ref:`core-metadata-name` (required; validated using :func:`~packaging.utils.canonicalize_name` and its @@ -770,6 +823,12 @@ def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata: """:external:ref:`core-metadata-maintainer-email`""" license: _Validator[str | None] = _Validator() """:external:ref:`core-metadata-license`""" + license_expression: _Validator[NormalizedLicenseExpression | None] = _Validator( + added="2.4" + ) + """:external:ref:`core-metadata-license-expression`""" + license_files: _Validator[list[str] | None] = _Validator(added="2.4") + """:external:ref:`core-metadata-license-file`""" classifiers: _Validator[list[str] | None] = _Validator(added="1.1") """:external:ref:`core-metadata-classifier`""" requires_dist: _Validator[list[requirements.Requirement] | None] = _Validator( diff --git a/setuptools/_vendor/packaging/specifiers.py b/setuptools/_vendor/packaging/specifiers.py index 2fa75f7abb..b30926af8b 100644 --- a/setuptools/_vendor/packaging/specifiers.py +++ b/setuptools/_vendor/packaging/specifiers.py @@ -234,7 +234,7 @@ def __init__(self, spec: str = "", prereleases: bool | None = None) -> None: """ match = self._regex.search(spec) if not match: - raise InvalidSpecifier(f"Invalid specifier: '{spec}'") + raise InvalidSpecifier(f"Invalid specifier: {spec!r}") self._spec: tuple[str, str] = ( match.group("operator").strip(), @@ -256,7 +256,7 @@ def prereleases(self) -> bool: # operators, and if they are if they are including an explicit # prerelease. operator, version = self._spec - if operator in ["==", ">=", "<=", "~=", "==="]: + if operator in ["==", ">=", "<=", "~=", "===", ">", "<"]: # The == specifier can include a trailing .*, if it does we # want to remove before parsing. if operator == "==" and version.endswith(".*"): @@ -694,12 +694,18 @@ class SpecifierSet(BaseSpecifier): specifiers (``>=3.0,!=3.1``), or no specifier at all. """ - def __init__(self, specifiers: str = "", prereleases: bool | None = None) -> None: + def __init__( + self, + specifiers: str | Iterable[Specifier] = "", + prereleases: bool | None = None, + ) -> None: """Initialize a SpecifierSet instance. :param specifiers: The string representation of a specifier or a comma-separated list of specifiers which will be parsed and normalized before use. + May also be an iterable of ``Specifier`` instances, which will be used + as is. :param prereleases: This tells the SpecifierSet if it should accept prerelease versions if applicable or not. The default of ``None`` will autodetect it from the @@ -710,12 +716,17 @@ def __init__(self, specifiers: str = "", prereleases: bool | None = None) -> Non raised. """ - # Split on `,` to break each individual specifier into it's own item, and - # strip each item to remove leading/trailing whitespace. - split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + if isinstance(specifiers, str): + # Split on `,` to break each individual specifier into its own item, and + # strip each item to remove leading/trailing whitespace. + split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] - # Make each individual specifier a Specifier and save in a frozen set for later. - self._specs = frozenset(map(Specifier, split_specifiers)) + # Make each individual specifier a Specifier and save in a frozen set + # for later. + self._specs = frozenset(map(Specifier, split_specifiers)) + else: + # Save the supplied specifiers in a frozen set. + self._specs = frozenset(specifiers) # Store our prereleases value so we can use it later to determine if # we accept prereleases or not. diff --git a/setuptools/_vendor/packaging/tags.py b/setuptools/_vendor/packaging/tags.py index 6667d29908..f5903402ab 100644 --- a/setuptools/_vendor/packaging/tags.py +++ b/setuptools/_vendor/packaging/tags.py @@ -25,7 +25,7 @@ logger = logging.getLogger(__name__) PythonVersion = Sequence[int] -MacVersion = Tuple[int, int] +AppleVersion = Tuple[int, int] INTERPRETER_SHORT_NAMES: dict[str, str] = { "python": "py", # Generic. @@ -47,7 +47,7 @@ class Tag: is also supported. """ - __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] + __slots__ = ["_abi", "_hash", "_interpreter", "_platform"] def __init__(self, interpreter: str, abi: str, platform: str) -> None: self._interpreter = interpreter.lower() @@ -235,9 +235,8 @@ def cpython_tags( if use_abi3: for minor_version in range(python_version[1] - 1, 1, -1): for platform_ in platforms: - interpreter = "cp{version}".format( - version=_version_nodot((python_version[0], minor_version)) - ) + version = _version_nodot((python_version[0], minor_version)) + interpreter = f"cp{version}" yield Tag(interpreter, "abi3", platform_) @@ -363,7 +362,7 @@ def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: return "i386" -def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> list[str]: +def _mac_binary_formats(version: AppleVersion, cpu_arch: str) -> list[str]: formats = [cpu_arch] if cpu_arch == "x86_64": if version < (10, 4): @@ -396,7 +395,7 @@ def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> list[str]: def mac_platforms( - version: MacVersion | None = None, arch: str | None = None + version: AppleVersion | None = None, arch: str | None = None ) -> Iterator[str]: """ Yields the platform tags for a macOS system. @@ -408,7 +407,7 @@ def mac_platforms( """ version_str, _, cpu_arch = platform.mac_ver() if version is None: - version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + version = cast("AppleVersion", tuple(map(int, version_str.split(".")[:2]))) if version == (10, 16): # When built against an older macOS SDK, Python will report macOS 10.16 # instead of the real version. @@ -424,7 +423,7 @@ def mac_platforms( stdout=subprocess.PIPE, text=True, ).stdout - version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + version = cast("AppleVersion", tuple(map(int, version_str.split(".")[:2]))) else: version = version if arch is None: @@ -435,24 +434,22 @@ def mac_platforms( if (10, 0) <= version and version < (11, 0): # Prior to Mac OS 11, each yearly release of Mac OS bumped the # "minor" version number. The major version was always 10. + major_version = 10 for minor_version in range(version[1], -1, -1): - compat_version = 10, minor_version + compat_version = major_version, minor_version binary_formats = _mac_binary_formats(compat_version, arch) for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=10, minor=minor_version, binary_format=binary_format - ) + yield f"macosx_{major_version}_{minor_version}_{binary_format}" if version >= (11, 0): # Starting with Mac OS 11, each yearly release bumps the major version # number. The minor versions are now the midyear updates. + minor_version = 0 for major_version in range(version[0], 10, -1): - compat_version = major_version, 0 + compat_version = major_version, minor_version binary_formats = _mac_binary_formats(compat_version, arch) for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=major_version, minor=0, binary_format=binary_format - ) + yield f"macosx_{major_version}_{minor_version}_{binary_format}" if version >= (11, 0): # Mac OS 11 on x86_64 is compatible with binaries from previous releases. @@ -462,25 +459,75 @@ def mac_platforms( # However, the "universal2" binary format can have a # macOS version earlier than 11.0 when the x86_64 part of the binary supports # that version of macOS. + major_version = 10 if arch == "x86_64": for minor_version in range(16, 3, -1): - compat_version = 10, minor_version + compat_version = major_version, minor_version binary_formats = _mac_binary_formats(compat_version, arch) for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) + yield f"macosx_{major_version}_{minor_version}_{binary_format}" else: for minor_version in range(16, 3, -1): - compat_version = 10, minor_version + compat_version = major_version, minor_version binary_format = "universal2" - yield "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) + yield f"macosx_{major_version}_{minor_version}_{binary_format}" + + +def ios_platforms( + version: AppleVersion | None = None, multiarch: str | None = None +) -> Iterator[str]: + """ + Yields the platform tags for an iOS system. + + :param version: A two-item tuple specifying the iOS version to generate + platform tags for. Defaults to the current iOS version. + :param multiarch: The CPU architecture+ABI to generate platform tags for - + (the value used by `sys.implementation._multiarch` e.g., + `arm64_iphoneos` or `x84_64_iphonesimulator`). Defaults to the current + multiarch value. + """ + if version is None: + # if iOS is the current platform, ios_ver *must* be defined. However, + # it won't exist for CPython versions before 3.13, which causes a mypy + # error. + _, release, _, _ = platform.ios_ver() # type: ignore[attr-defined, unused-ignore] + version = cast("AppleVersion", tuple(map(int, release.split(".")[:2]))) + + if multiarch is None: + multiarch = sys.implementation._multiarch + multiarch = multiarch.replace("-", "_") + + ios_platform_template = "ios_{major}_{minor}_{multiarch}" + + # Consider any iOS major.minor version from the version requested, down to + # 12.0. 12.0 is the first iOS version that is known to have enough features + # to support CPython. Consider every possible minor release up to X.9. There + # highest the minor has ever gone is 8 (14.8 and 15.8) but having some extra + # candidates that won't ever match doesn't really hurt, and it saves us from + # having to keep an explicit list of known iOS versions in the code. Return + # the results descending order of version number. + + # If the requested major version is less than 12, there won't be any matches. + if version[0] < 12: + return + + # Consider the actual X.Y version that was requested. + yield ios_platform_template.format( + major=version[0], minor=version[1], multiarch=multiarch + ) + + # Consider every minor version from X.0 to the minor version prior to the + # version requested by the platform. + for minor in range(version[1] - 1, -1, -1): + yield ios_platform_template.format( + major=version[0], minor=minor, multiarch=multiarch + ) + + for major in range(version[0] - 1, 11, -1): + for minor in range(9, -1, -1): + yield ios_platform_template.format( + major=major, minor=minor, multiarch=multiarch + ) def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: @@ -512,6 +559,8 @@ def platform_tags() -> Iterator[str]: """ if platform.system() == "Darwin": return mac_platforms() + elif platform.system() == "iOS": + return ios_platforms() elif platform.system() == "Linux": return _linux_platforms() else: diff --git a/setuptools/_vendor/packaging/utils.py b/setuptools/_vendor/packaging/utils.py index d33da5bb8b..23450953df 100644 --- a/setuptools/_vendor/packaging/utils.py +++ b/setuptools/_vendor/packaging/utils.py @@ -4,11 +4,12 @@ from __future__ import annotations +import functools import re from typing import NewType, Tuple, Union, cast from .tags import Tag, parse_tag -from .version import InvalidVersion, Version +from .version import InvalidVersion, Version, _TrimmedRelease BuildTag = Union[Tuple[()], Tuple[int, str]] NormalizedName = NewType("NormalizedName", str) @@ -54,52 +55,40 @@ def is_normalized_name(name: str) -> bool: return _normalized_regex.match(name) is not None +@functools.singledispatch def canonicalize_version( version: Version | str, *, strip_trailing_zero: bool = True ) -> str: """ - This is very similar to Version.__str__, but has one subtle difference - with the way it handles the release segment. - """ - if isinstance(version, str): - try: - parsed = Version(version) - except InvalidVersion: - # Legacy versions cannot be normalized - return version - else: - parsed = version - - parts = [] + Return a canonical form of a version as a string. - # Epoch - if parsed.epoch != 0: - parts.append(f"{parsed.epoch}!") + >>> canonicalize_version('1.0.1') + '1.0.1' - # Release segment - release_segment = ".".join(str(x) for x in parsed.release) - if strip_trailing_zero: - # NB: This strips trailing '.0's to normalize - release_segment = re.sub(r"(\.0)+$", "", release_segment) - parts.append(release_segment) + Per PEP 625, versions may have multiple canonical forms, differing + only by trailing zeros. - # Pre-release - if parsed.pre is not None: - parts.append("".join(str(x) for x in parsed.pre)) + >>> canonicalize_version('1.0.0') + '1' + >>> canonicalize_version('1.0.0', strip_trailing_zero=False) + '1.0.0' - # Post-release - if parsed.post is not None: - parts.append(f".post{parsed.post}") + Invalid versions are returned unaltered. - # Development release - if parsed.dev is not None: - parts.append(f".dev{parsed.dev}") + >>> canonicalize_version('foo bar baz') + 'foo bar baz' + """ + return str(_TrimmedRelease(str(version)) if strip_trailing_zero else version) - # Local version segment - if parsed.local is not None: - parts.append(f"+{parsed.local}") - return "".join(parts) +@canonicalize_version.register +def _(version: str, *, strip_trailing_zero: bool = True) -> str: + try: + parsed = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version + return canonicalize_version(parsed, strip_trailing_zero=strip_trailing_zero) def parse_wheel_filename( @@ -107,28 +96,28 @@ def parse_wheel_filename( ) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]: if not filename.endswith(".whl"): raise InvalidWheelFilename( - f"Invalid wheel filename (extension must be '.whl'): {filename}" + f"Invalid wheel filename (extension must be '.whl'): {filename!r}" ) filename = filename[:-4] dashes = filename.count("-") if dashes not in (4, 5): raise InvalidWheelFilename( - f"Invalid wheel filename (wrong number of parts): {filename}" + f"Invalid wheel filename (wrong number of parts): {filename!r}" ) parts = filename.split("-", dashes - 2) name_part = parts[0] # See PEP 427 for the rules on escaping the project name. if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: - raise InvalidWheelFilename(f"Invalid project name: {filename}") + raise InvalidWheelFilename(f"Invalid project name: {filename!r}") name = canonicalize_name(name_part) try: version = Version(parts[1]) except InvalidVersion as e: raise InvalidWheelFilename( - f"Invalid wheel filename (invalid version): {filename}" + f"Invalid wheel filename (invalid version): {filename!r}" ) from e if dashes == 5: @@ -136,7 +125,7 @@ def parse_wheel_filename( build_match = _build_tag_regex.match(build_part) if build_match is None: raise InvalidWheelFilename( - f"Invalid build number: {build_part} in '{filename}'" + f"Invalid build number: {build_part} in {filename!r}" ) build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) else: @@ -153,14 +142,14 @@ def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]: else: raise InvalidSdistFilename( f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" - f" {filename}" + f" {filename!r}" ) # We are requiring a PEP 440 version, which cannot contain dashes, # so we split on the last dash. name_part, sep, version_part = file_stem.rpartition("-") if not sep: - raise InvalidSdistFilename(f"Invalid sdist filename: {filename}") + raise InvalidSdistFilename(f"Invalid sdist filename: {filename!r}") name = canonicalize_name(name_part) @@ -168,7 +157,7 @@ def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]: version = Version(version_part) except InvalidVersion as e: raise InvalidSdistFilename( - f"Invalid sdist filename (invalid version): {filename}" + f"Invalid sdist filename (invalid version): {filename!r}" ) from e return (name, version) diff --git a/setuptools/_vendor/packaging/version.py b/setuptools/_vendor/packaging/version.py index 46bc261308..c9bbda20e4 100644 --- a/setuptools/_vendor/packaging/version.py +++ b/setuptools/_vendor/packaging/version.py @@ -15,7 +15,7 @@ from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType -__all__ = ["VERSION_PATTERN", "parse", "Version", "InvalidVersion"] +__all__ = ["VERSION_PATTERN", "InvalidVersion", "Version", "parse"] LocalType = Tuple[Union[int, str], ...] @@ -199,7 +199,7 @@ def __init__(self, version: str) -> None: # Validate the version and parse it into pieces match = self._regex.search(version) if not match: - raise InvalidVersion(f"Invalid version: '{version}'") + raise InvalidVersion(f"Invalid version: {version!r}") # Store the parsed out pieces of the version self._version = _Version( @@ -232,7 +232,7 @@ def __repr__(self) -> str: return f"" def __str__(self) -> str: - """A string representation of the version that can be rounded-tripped. + """A string representation of the version that can be round-tripped. >>> str(Version("1.0a5")) '1.0a5' @@ -350,8 +350,8 @@ def public(self) -> str: '1.2.3' >>> Version("1.2.3+abc").public '1.2.3' - >>> Version("1.2.3+abc.dev1").public - '1.2.3' + >>> Version("1!1.2.3dev1+abc").public + '1!1.2.3.dev1' """ return str(self).split("+", 1)[0] @@ -363,7 +363,7 @@ def base_version(self) -> str: '1.2.3' >>> Version("1.2.3+abc").base_version '1.2.3' - >>> Version("1!1.2.3+abc.dev1").base_version + >>> Version("1!1.2.3dev1+abc").base_version '1!1.2.3' The "base version" is the public version of the project without any pre or post @@ -451,6 +451,23 @@ def micro(self) -> int: return self.release[2] if len(self.release) >= 3 else 0 +class _TrimmedRelease(Version): + @property + def release(self) -> tuple[int, ...]: + """ + Release segment without any trailing zeros. + + >>> _TrimmedRelease('1.0.0').release + (1,) + >>> _TrimmedRelease('0.0').release + (0,) + """ + rel = super().release + nonzeros = (index for index, val in enumerate(rel) if val) + last_nonzero = max(nonzeros, default=0) + return rel[: last_nonzero + 1] + + def _parse_letter_version( letter: str | None, number: str | bytes | SupportsInt | None ) -> tuple[str, int] | None: @@ -476,7 +493,9 @@ def _parse_letter_version( letter = "post" return letter, int(number) - if not letter and number: + + assert not letter + if number: # We assume if we are given a number, but we are not given a letter # then this is using the implicit post release syntax (e.g. 1.0-1) letter = "post" diff --git a/setuptools/command/bdist_wheel.py b/setuptools/command/bdist_wheel.py index ff83ceeb79..bef4858d0a 100644 --- a/setuptools/command/bdist_wheel.py +++ b/setuptools/command/bdist_wheel.py @@ -9,7 +9,6 @@ import os import re import shutil -import stat import struct import sys import sysconfig @@ -18,23 +17,19 @@ from email.generator import BytesGenerator, Generator from email.policy import EmailPolicy from glob import iglob -from shutil import rmtree -from typing import TYPE_CHECKING, Callable, Literal, cast +from typing import Literal, cast from zipfile import ZIP_DEFLATED, ZIP_STORED from packaging import tags, version as _packaging_version from wheel.metadata import pkginfo_to_metadata from wheel.wheelfile import WheelFile -from .. import Command, __version__ +from .. import Command, __version__, _shutil from ..warnings import SetuptoolsDeprecationWarning from .egg_info import egg_info as egg_info_cls from distutils import log -if TYPE_CHECKING: - from _typeshed import ExcInfo - def safe_name(name: str) -> str: """Convert an arbitrary string to a standard distribution name @@ -148,21 +143,6 @@ def safer_version(version: str) -> str: return safe_version(version).replace("-", "_") -def remove_readonly( - func: Callable[..., object], - path: str, - excinfo: ExcInfo, -) -> None: - remove_readonly_exc(func, path, excinfo[1]) - - -def remove_readonly_exc( - func: Callable[..., object], path: str, exc: BaseException -) -> None: - os.chmod(path, stat.S_IWRITE) - func(path) - - class bdist_wheel(Command): description = "create a wheel distribution" @@ -458,7 +438,7 @@ def run(self) -> None: shutil.copytree(self.dist_info_dir, distinfo_dir) # Egg info is still generated, so remove it now to avoid it getting # copied into the wheel. - shutil.rmtree(self.egginfo_dir) + _shutil.rmtree(self.egginfo_dir) else: # Convert the generated egg-info into dist-info. self.egg2dist(self.egginfo_dir, distinfo_dir) @@ -483,10 +463,7 @@ def run(self) -> None: if not self.keep_temp: log.info(f"removing {self.bdist_dir}") if not self.dry_run: - if sys.version_info < (3, 12): - rmtree(self.bdist_dir, onerror=remove_readonly) - else: - rmtree(self.bdist_dir, onexc=remove_readonly_exc) + _shutil.rmtree(self.bdist_dir) def write_wheelfile( self, wheelfile_base: str, generator: str = f"setuptools ({__version__})" @@ -570,7 +547,7 @@ def egg2dist(self, egginfo_path: str, distinfo_path: str) -> None: def adios(p: str) -> None: """Appropriately delete directory, file or link.""" if os.path.exists(p) and not os.path.islink(p) and os.path.isdir(p): - shutil.rmtree(p) + _shutil.rmtree(p) elif os.path.exists(p): os.unlink(p) diff --git a/setuptools/command/dist_info.py b/setuptools/command/dist_info.py index 3ad27ed708..0192ebb260 100644 --- a/setuptools/command/dist_info.py +++ b/setuptools/command/dist_info.py @@ -10,6 +10,7 @@ from typing import cast from .. import _normalization +from .._shutil import rmdir as _rm from .egg_info import egg_info as egg_info_cls from distutils import log @@ -100,8 +101,3 @@ def run(self) -> None: # TODO: if bdist_wheel if merged into setuptools, just add "keep_egg_info" there with self._maybe_bkp_dir(egg_info_dir, self.keep_egg_info): bdist_wheel.egg2dist(egg_info_dir, self.dist_info_dir) - - -def _rm(dir_name, **opts): - if os.path.isdir(dir_name): - shutil.rmtree(dir_name, **opts) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index e2acad23b9..8ed6ee6d6a 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -34,7 +34,7 @@ from collections.abc import Iterable from glob import glob from sysconfig import get_path -from typing import TYPE_CHECKING, Callable, NoReturn, TypedDict, TypeVar +from typing import TYPE_CHECKING, NoReturn, TypedDict from jaraco.text import yield_lines @@ -63,7 +63,8 @@ from setuptools.wheel import Wheel from .._path import ensure_directory -from ..compat import py39, py311, py312 +from .._shutil import attempt_chmod_verbose as chmod, rmtree as _rmtree +from ..compat import py39, py312 from distutils import dir_util, log from distutils.command import install @@ -89,8 +90,6 @@ 'get_exe_prefixes', ] -_T = TypeVar("_T") - def is_64bit(): return struct.calcsize("P") == 8 @@ -1789,16 +1788,6 @@ def _first_line_re(): return re.compile(first_line_re.pattern.decode()) -# Must match shutil._OnExcCallback -def auto_chmod(func: Callable[..., _T], arg: str, exc: BaseException) -> _T: - """shutils onexc callback to automatically call chmod for certain functions.""" - # Only retry for scenarios known to have an issue - if func in [os.unlink, os.remove] and os.name == 'nt': - chmod(arg, stat.S_IWRITE) - return func(arg) - raise exc - - def update_dist_caches(dist_path, fix_zipimporter_caches): """ Fix any globally cached `dist_path` related data @@ -2021,24 +2010,6 @@ def is_python_script(script_text, filename): return False # Not any Python I can recognize -try: - from os import ( - chmod as _chmod, # pyright: ignore[reportAssignmentType] # Losing type-safety w/ pyright, but that's ok - ) -except ImportError: - # Jython compatibility - def _chmod(*args: object, **kwargs: object) -> None: # type: ignore[misc] # Mypy reuses the imported definition anyway - pass - - -def chmod(path, mode): - log.debug("changing mode of %s to %o", path, mode) - try: - _chmod(path, mode) - except OSError as e: - log.debug("chmod failed: %s", e) - - class _SplitArgs(TypedDict, total=False): comments: bool posix: bool @@ -2350,10 +2321,6 @@ def load_launcher_manifest(name): return manifest.decode('utf-8') % vars() -def _rmtree(path, ignore_errors: bool = False, onexc=auto_chmod): - return py311.shutil_rmtree(path, ignore_errors, onexc) - - def current_umask(): tmp = os.umask(0o022) os.umask(tmp) diff --git a/setuptools/command/editable_wheel.py b/setuptools/command/editable_wheel.py index d4ef24038d..926c5ab9b8 100644 --- a/setuptools/command/editable_wheel.py +++ b/setuptools/command/editable_wheel.py @@ -27,7 +27,7 @@ from types import TracebackType from typing import TYPE_CHECKING, Protocol, TypeVar, cast -from .. import Command, _normalization, _path, errors, namespaces +from .. import Command, _normalization, _path, _shutil, errors, namespaces from .._path import StrPath from ..compat import py312 from ..discovery import find_package_path @@ -773,7 +773,7 @@ def _is_nested(pkg: str, pkg_path: str, parent: str, parent_path: str) -> bool: def _empty_dir(dir_: _P) -> _P: """Create a directory ensured to be empty. Existing files may be removed.""" - shutil.rmtree(dir_, ignore_errors=True) + _shutil.rmtree(dir_, ignore_errors=True) os.makedirs(dir_) return dir_ diff --git a/setuptools/command/rotate.py b/setuptools/command/rotate.py index c10e8d5024..acdce07baa 100644 --- a/setuptools/command/rotate.py +++ b/setuptools/command/rotate.py @@ -1,10 +1,9 @@ from __future__ import annotations import os -import shutil from typing import ClassVar -from setuptools import Command +from .. import Command, _shutil from distutils import log from distutils.errors import DistutilsOptionError @@ -61,6 +60,6 @@ def run(self) -> None: log.info("Deleting %s", f) if not self.dry_run: if os.path.isdir(f): - shutil.rmtree(f) + _shutil.rmtree(f) else: os.unlink(f) diff --git a/setuptools/tests/test_bdist_wheel.py b/setuptools/tests/test_bdist_wheel.py index 3dfa9c850c..d51dfbeb6d 100644 --- a/setuptools/tests/test_bdist_wheel.py +++ b/setuptools/tests/test_bdist_wheel.py @@ -11,7 +11,6 @@ import sysconfig from contextlib import suppress from inspect import cleandoc -from unittest.mock import Mock from zipfile import ZipFile import jaraco.path @@ -19,12 +18,7 @@ from packaging import tags import setuptools -from setuptools.command.bdist_wheel import ( - bdist_wheel, - get_abi_tag, - remove_readonly, - remove_readonly_exc, -) +from setuptools.command.bdist_wheel import bdist_wheel, get_abi_tag from setuptools.dist import Distribution from setuptools.warnings import SetuptoolsDeprecationWarning @@ -510,29 +504,6 @@ def test_platform_with_space(dummy_dist, monkeypatch): bdist_wheel_cmd(plat_name="isilon onefs").run() -def test_rmtree_readonly(monkeypatch, tmp_path): - """Verify onerr works as expected""" - - bdist_dir = tmp_path / "with_readonly" - bdist_dir.mkdir() - some_file = bdist_dir.joinpath("file.txt") - some_file.touch() - some_file.chmod(stat.S_IREAD) - - expected_count = 1 if sys.platform.startswith("win") else 0 - - if sys.version_info < (3, 12): - count_remove_readonly = Mock(side_effect=remove_readonly) - shutil.rmtree(bdist_dir, onerror=count_remove_readonly) - assert count_remove_readonly.call_count == expected_count - else: - count_remove_readonly_exc = Mock(side_effect=remove_readonly_exc) - shutil.rmtree(bdist_dir, onexc=count_remove_readonly_exc) - assert count_remove_readonly_exc.call_count == expected_count - - assert not bdist_dir.is_dir() - - def test_data_dir_with_tag_build(monkeypatch, tmp_path): """ Setuptools allow authors to set PEP 440's local version segments diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py index be953079f5..7b8cb91469 100644 --- a/setuptools/tests/test_dist.py +++ b/setuptools/tests/test_dist.py @@ -144,8 +144,12 @@ def test_check_specifier(): dist = Distribution(attrs) check_specifier(dist, attrs, attrs['python_requires']) - # invalid specifier value attrs = {'name': 'foo', 'python_requires': ['>=3.0', '!=3.1']} + dist = Distribution(attrs) + check_specifier(dist, attrs, attrs['python_requires']) + + # invalid specifier value + attrs = {'name': 'foo', 'python_requires': '>=invalid-version'} with pytest.raises(DistutilsSetupError): dist = Distribution(attrs) diff --git a/setuptools/tests/test_shutil_wrapper.py b/setuptools/tests/test_shutil_wrapper.py new file mode 100644 index 0000000000..74ff7e9a89 --- /dev/null +++ b/setuptools/tests/test_shutil_wrapper.py @@ -0,0 +1,23 @@ +import stat +import sys +from unittest.mock import Mock + +from setuptools import _shutil + + +def test_rmtree_readonly(monkeypatch, tmp_path): + """Verify onerr works as expected""" + + tmp_dir = tmp_path / "with_readonly" + tmp_dir.mkdir() + some_file = tmp_dir.joinpath("file.txt") + some_file.touch() + some_file.chmod(stat.S_IREAD) + + expected_count = 1 if sys.platform.startswith("win") else 0 + chmod_fn = Mock(wraps=_shutil.attempt_chmod_verbose) + monkeypatch.setattr(_shutil, "attempt_chmod_verbose", chmod_fn) + + _shutil.rmtree(tmp_dir) + assert chmod_fn.call_count == expected_count + assert not tmp_dir.is_dir()