From 28bfe354ff23e387a40126b0ead18e3f0e9e3751 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 13 Oct 2024 18:56:07 -0400 Subject: [PATCH 01/10] release Hatch v1.13.0 --- docs/history/hatch.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/history/hatch.md b/docs/history/hatch.md index cedeee268..486d1fb61 100644 --- a/docs/history/hatch.md +++ b/docs/history/hatch.md @@ -19,6 +19,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Build environments can now be configured, the default build environment is `hatch-build` - The environment interface now has the following methods and properties in order to better support builds on remote machines: `project_root`, `sep`, `pathsep`, `fs_context` +## [1.13.0](https://github.com/pypa/hatch/releases/tag/hatch-v1.13.0) - 2024-10-13 ## {: #hatch-v1.13.0 } + +***Added:*** + +- Support managing Python 3.13 distributions + ## [1.12.0](https://github.com/pypa/hatch/releases/tag/hatch-v1.12.0) - 2024-05-28 ## {: #hatch-v1.12.0 } ***Changed:*** From d33be840bc07fb4fbcf24889a0f44bd14093d7de Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 13 Oct 2024 18:54:00 -0400 Subject: [PATCH 02/10] Support managing Python 3.13 distributions (#1753) --- .github/workflows/test.yml | 4 +- docs/plugins/environment/virtual.md | 11 +- docs/tutorials/python/manage.md | 12 +- docs/why.md | 1 - pyproject.toml | 1 + scripts/update_distributions.py | 8 +- src/hatch/env/internal/test.py | 2 +- src/hatch/python/distributions.py | 411 +++++++++++++--------- src/hatch/python/resolve.py | 31 +- src/hatch/template/files_feature_ci.py | 2 +- tests/cli/env/test_show.py | 1 + tests/helpers/templates/new/feature_ci.py | 2 +- tests/python/test_resolve.py | 143 ++++---- 13 files changed, 361 insertions(+), 268 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 67d0864a5..1e3052598 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] steps: - uses: actions/checkout@v4 @@ -115,7 +115,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] steps: - uses: actions/checkout@v4 diff --git a/docs/plugins/environment/virtual.md b/docs/plugins/environment/virtual.md index 624349840..a20ebd6bc 100644 --- a/docs/plugins/environment/virtual.md +++ b/docs/plugins/environment/virtual.md @@ -68,15 +68,16 @@ The following options are recognized for internal Python resolution. | `3.10` | | `3.11` | | `3.12` | +| `3.13` | The source of distributions is the [python-build-standalone](https://github.com/indygreg/python-build-standalone) project. -Some distributions have [variants](https://gregoryszorc.com/docs/python-build-standalone/main/running.html) that may be configured with the `HATCH_PYTHON_VARIANT_` environment variable where `` is the uppercase version of one of the following: +Some distributions have [variants](https://gregoryszorc.com/docs/python-build-standalone/main/running.html) that may be configured. Options may be combined. -| Platform | Options | -| --- | --- | -| Linux |
  • v1
  • v2
  • v3 (default)
  • v4
| -| Windows |
  • shared (default)
  • static
| +| Option | Platforms | Allowed values | +| --- | --- | --- | +| `HATCH_PYTHON_VARIANT_CPU` |
  • Linux
|
  • v1
  • v2
  • v3 (default)
  • v4
| +| `HATCH_PYTHON_VARIANT_GIL` |
  • Linux
  • Windows
  • macOS
|
  • freethreaded
| ### PyPy diff --git a/docs/tutorials/python/manage.md b/docs/tutorials/python/manage.md index 257c69f01..43ca6c650 100644 --- a/docs/tutorials/python/manage.md +++ b/docs/tutorials/python/manage.md @@ -70,7 +70,7 @@ $ hatch python show ┏━━━━━━┳━━━━━━━━━┓ ┃ Name ┃ Version ┃ ┡━━━━━━╇━━━━━━━━━┩ -│ 3.12 │ 3.12.3 │ +│ 3.12 │ 3.12.7 │ └──────┴─────────┘ Available ┏━━━━━━━━━━┳━━━━━━━━━┓ @@ -78,13 +78,15 @@ $ hatch python show ┡━━━━━━━━━━╇━━━━━━━━━┩ │ 3.7 │ 3.7.9 │ ├──────────┼─────────┤ -│ 3.8 │ 3.8.19 │ +│ 3.8 │ 3.8.20 │ ├──────────┼─────────┤ -│ 3.9 │ 3.9.19 │ +│ 3.9 │ 3.9.20 │ ├──────────┼─────────┤ -│ 3.10 │ 3.10.14 │ +│ 3.10 │ 3.10.15 │ ├──────────┼─────────┤ -│ 3.11 │ 3.11.9 │ +│ 3.11 │ 3.11.10 │ +├──────────┼─────────┤ +│ 3.13 │ 3.13.0 │ ├──────────┼─────────┤ │ pypy2.7 │ 7.3.15 │ ├──────────┼─────────┤ diff --git a/docs/why.md b/docs/why.md index 95e7cd57c..aec67e1bc 100644 --- a/docs/why.md +++ b/docs/why.md @@ -33,7 +33,6 @@ Here we compare to both `tox` and `nox`. At a high level, there are a few common In Hatch, [environments](environment.md) are treated as isolated areas where you can execute arbitrary commands at runtime. For example, you can define a single test environment with named [scripts](config/environment/overview.md#scripts) that runs unit vs non-unit tests, each command being potentially very long but named however you wish so you get to control the interface. Since environments are treated as places where work is performed, you can also [spawn a shell](environment.md#entering-environments) into any which will execute a subprocess that automatically drops into your [shell of choice](config/hatch.md#shell). Your shell will be configured appropriately like `python` on PATH being updated and the prompt being changed to reflect the chosen environment. - **Configuration:** - - `tox` only supports INI configuration and if one desires putting that in the standard `pyproject.toml` file then [it must be](https://tox.wiki/en/4.11.4/config.html#pyproject-toml) a multi-line string containing the INI config which would preclude syntax highlighting. Hatch allows for TOML-based config just like most other tools in the Python ecosystem. - `nox` config is defined in Python which often leads to increased verbosity and makes it challenging to onboard folks compared to a standardized format with known behaviors. - **Extensibility:** - `tox` allows for [extending](https://tox.wiki/en/4.11.4/plugins_api.html) most aspects of its functionality however the API is so low-level and attached to internals that creating plugins may be challenging. For example, [here](https://github.com/DataDog/integrations-core/blob/4f4cf10613797e97e7155c75859532a0732d1dff/datadog_checks_dev/datadog_checks/dev/plugin/tox.py) is a `tox` plugin that was [migrated](https://github.com/DataDog/integrations-core/blob/4eb2a1d530bcf810542cf9e45b48fadc7057301c/datadog_checks_dev/datadog_checks/dev/plugin/hatch/environment_collector.py#L100-L148) to an equivalent Hatch [environment collector plugin](plugins/environment-collector/reference.md). diff --git a/pyproject.toml b/pyproject.toml index 5d8b689e2..4fd7e3da8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Build Tools", diff --git a/scripts/update_distributions.py b/scripts/update_distributions.py index 0767d16c0..6f3e89de9 100644 --- a/scripts/update_distributions.py +++ b/scripts/update_distributions.py @@ -11,8 +11,8 @@ OUTPUT_FILE = ROOT / 'src' / 'hatch' / 'python' / 'distributions.py' ARCHES = {('linux', 'x86'): 'i686', ('windows', 'x86_64'): 'amd64', ('windows', 'x86'): 'i386'} -# system, architecture, ABI, variant -MAX_IDENTIFIER_COMPONENTS = 4 +# system, architecture, ABI, CPU variant, GIL variant +MAX_IDENTIFIER_COMPONENTS = 5 def parse_distributions(contents: str, constant: str): @@ -34,9 +34,9 @@ def parse_distributions(contents: str, constant: str): elif os == 'macos' and arch == 'aarch64': arch = 'arm64' - # Force everything to have a variant to maintain structure + # Force everything to have the proper number of variants to maintain structure if len(data) != MAX_IDENTIFIER_COMPONENTS: - data.append('') + data.extend(('', '')) data[1] = ARCHES.get((os, arch), arch) yield identifier, tuple(data), source diff --git a/src/hatch/env/internal/test.py b/src/hatch/env/internal/test.py index da2e650aa..d507b66e5 100644 --- a/src/hatch/env/internal/test.py +++ b/src/hatch/env/internal/test.py @@ -21,5 +21,5 @@ def get_default_config() -> dict[str, Any]: 'cov-combine': 'coverage combine', 'cov-report': 'coverage report', }, - 'matrix': [{'python': ['3.12', '3.11', '3.10', '3.9', '3.8']}], + 'matrix': [{'python': ['3.13', '3.12', '3.11', '3.10', '3.9', '3.8']}], } diff --git a/src/hatch/python/distributions.py b/src/hatch/python/distributions.py index 793bd643d..dc35dfe52 100644 --- a/src/hatch/python/distributions.py +++ b/src/hatch/python/distributions.py @@ -8,225 +8,288 @@ '3.10', '3.11', '3.12', + '3.13', 'pypy2.7', 'pypy3.9', 'pypy3.10', ) DISTRIBUTIONS: dict[str, dict[tuple[str, ...], str]] = { + '3.13': { + ('linux', 'aarch64', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'aarch64', 'gnu', '', 'freethreaded'): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-aarch64-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst', + ('linux', 'armv7', 'gnueabi', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz', + ('linux', 'armv7', 'gnueabi', '', 'freethreaded'): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-armv7-unknown-linux-gnueabi-freethreaded%2Bnoopt-full.tar.zst', + ('linux', 'armv7', 'gnueabihf', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz', + ('linux', 'armv7', 'gnueabihf', '', 'freethreaded'): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-armv7-unknown-linux-gnueabihf-freethreaded%2Bnoopt-full.tar.zst', + ('linux', 'ppc64le', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'ppc64le', 'gnu', '', 'freethreaded'): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-ppc64le-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst', + ('linux', 's390x', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-s390x-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 's390x', 'gnu', '', 'freethreaded'): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-s390x-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst', + ('linux', 'x86_64', 'gnu', 'v1', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v1', 'freethreaded'): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64-unknown-linux-gnu-freethreaded%2Bpgo-full.tar.zst', + ('linux', 'x86_64', 'gnu', 'v2', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v2', 'freethreaded'): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64_v2-unknown-linux-gnu-freethreaded%2Bpgo-full.tar.zst', + ('linux', 'x86_64', 'gnu', 'v3', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v3', 'freethreaded'): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64_v3-unknown-linux-gnu-freethreaded%2Bpgo-full.tar.zst', + ('linux', 'x86_64', 'gnu', 'v4', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v4', 'freethreaded'): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64_v4-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst', + ('linux', 'x86_64', 'musl', 'v1', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v2', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v3', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v4', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz', + ('windows', 'i386', 'msvc', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-i686-pc-windows-msvc-install_only_stripped.tar.gz', + ('windows', 'i386', 'msvc', '', 'freethreaded'): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-i686-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst', + ('windows', 'amd64', 'msvc', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64-pc-windows-msvc-install_only_stripped.tar.gz', + ('windows', 'amd64', 'msvc', '', 'freethreaded'): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst', + ('macos', 'arm64', '', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-aarch64-apple-darwin-install_only_stripped.tar.gz', + ('macos', 'arm64', '', '', 'freethreaded'): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-aarch64-apple-darwin-freethreaded%2Bpgo-full.tar.zst', + ('macos', 'x86_64', '', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64-apple-darwin-install_only_stripped.tar.gz', + ('macos', 'x86_64', '', '', 'freethreaded'): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.13.0%2B20241008-x86_64-apple-darwin-freethreaded%2Bpgo-full.tar.zst', + }, '3.12': { - ('linux', 'aarch64', 'gnu', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-aarch64-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'armv7', 'gnueabi', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-armv7-unknown-linux-gnueabi-install_only.tar.gz', - ('linux', 'armv7', 'gnueabihf', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-armv7-unknown-linux-gnueabihf-install_only.tar.gz', - ('linux', 'ppc64le', 'gnu', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-ppc64le-unknown-linux-gnu-install_only.tar.gz', - ('linux', 's390x', 'gnu', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-s390x-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v1'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-x86_64-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v2'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-x86_64_v2-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v3'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-x86_64_v3-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v4'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-x86_64_v4-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v1'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-x86_64-unknown-linux-musl-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v2'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-x86_64_v2-unknown-linux-musl-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v3'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-x86_64_v3-unknown-linux-musl-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v4'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-x86_64_v4-unknown-linux-musl-install_only.tar.gz', - ('windows', 'i386', 'msvc', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-i686-pc-windows-msvc-install_only.tar.gz', - ('windows', 'amd64', 'msvc', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-x86_64-pc-windows-msvc-install_only.tar.gz', - ('macos', 'arm64', '', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-aarch64-apple-darwin-install_only.tar.gz', - ('macos', 'x86_64', '', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.12.3%2B20240415-x86_64-apple-darwin-install_only.tar.gz', + ('linux', 'aarch64', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'armv7', 'gnueabi', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz', + ('linux', 'armv7', 'gnueabihf', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz', + ('linux', 'ppc64le', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 's390x', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-s390x-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v1', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v2', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v3', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v4', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v1', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-x86_64-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v2', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v3', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v4', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz', + ('windows', 'i386', 'msvc', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-i686-pc-windows-msvc-install_only_stripped.tar.gz', + ('windows', 'amd64', 'msvc', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-x86_64-pc-windows-msvc-install_only_stripped.tar.gz', + ('macos', 'arm64', '', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-aarch64-apple-darwin-install_only_stripped.tar.gz', + ('macos', 'x86_64', '', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.12.7%2B20241008-x86_64-apple-darwin-install_only_stripped.tar.gz', }, '3.11': { - ('linux', 'aarch64', 'gnu', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-aarch64-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'armv7', 'gnueabi', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-armv7-unknown-linux-gnueabi-install_only.tar.gz', - ('linux', 'armv7', 'gnueabihf', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-armv7-unknown-linux-gnueabihf-install_only.tar.gz', - ('linux', 'ppc64le', 'gnu', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-ppc64le-unknown-linux-gnu-install_only.tar.gz', - ('linux', 's390x', 'gnu', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-s390x-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'i686', 'gnu', ''): + ('linux', 'aarch64', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'armv7', 'gnueabi', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz', + ('linux', 'armv7', 'gnueabihf', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz', + ('linux', 'ppc64le', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 's390x', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-s390x-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v1', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v2', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v3', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v4', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v1', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-x86_64-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v2', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v3', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v4', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz', + ('windows', 'i386', 'msvc', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-i686-pc-windows-msvc-install_only_stripped.tar.gz', + ('windows', 'amd64', 'msvc', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-x86_64-pc-windows-msvc-install_only_stripped.tar.gz', + ('macos', 'arm64', '', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-aarch64-apple-darwin-install_only_stripped.tar.gz', + ('macos', 'x86_64', '', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.11.10%2B20241008-x86_64-apple-darwin-install_only_stripped.tar.gz', + ('linux', 'i686', 'gnu', '', ''): 'https://github.com/indygreg/python-build-standalone/releases/download/20230826/cpython-3.11.5%2B20230826-i686-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v1'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-x86_64-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v2'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-x86_64_v2-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v3'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-x86_64_v3-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v4'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-x86_64_v4-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v1'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-x86_64-unknown-linux-musl-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v2'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-x86_64_v2-unknown-linux-musl-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v3'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-x86_64_v3-unknown-linux-musl-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v4'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-x86_64_v4-unknown-linux-musl-install_only.tar.gz', - ('windows', 'i386', 'msvc', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-i686-pc-windows-msvc-install_only.tar.gz', - ('windows', 'amd64', 'msvc', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-x86_64-pc-windows-msvc-install_only.tar.gz', - ('macos', 'arm64', '', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-aarch64-apple-darwin-install_only.tar.gz', - ('macos', 'x86_64', '', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9%2B20240415-x86_64-apple-darwin-install_only.tar.gz', }, '3.10': { - ('linux', 'aarch64', 'gnu', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-aarch64-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'armv7', 'gnueabi', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-armv7-unknown-linux-gnueabi-install_only.tar.gz', - ('linux', 'armv7', 'gnueabihf', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-armv7-unknown-linux-gnueabihf-install_only.tar.gz', - ('linux', 'ppc64le', 'gnu', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-ppc64le-unknown-linux-gnu-install_only.tar.gz', - ('linux', 's390x', 'gnu', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-s390x-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'i686', 'gnu', ''): + ('linux', 'aarch64', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'armv7', 'gnueabi', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz', + ('linux', 'armv7', 'gnueabihf', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz', + ('linux', 'ppc64le', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 's390x', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-s390x-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v1', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v2', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v3', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v4', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v1', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-x86_64-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v2', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v3', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v4', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz', + ('windows', 'i386', 'msvc', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-i686-pc-windows-msvc-install_only_stripped.tar.gz', + ('windows', 'amd64', 'msvc', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-x86_64-pc-windows-msvc-install_only_stripped.tar.gz', + ('macos', 'arm64', '', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-aarch64-apple-darwin-install_only_stripped.tar.gz', + ('macos', 'x86_64', '', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.10.15%2B20241008-x86_64-apple-darwin-install_only_stripped.tar.gz', + ('linux', 'i686', 'gnu', '', ''): 'https://github.com/indygreg/python-build-standalone/releases/download/20230826/cpython-3.10.13%2B20230826-i686-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v1'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-x86_64-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v2'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-x86_64_v2-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v3'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-x86_64_v3-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v4'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-x86_64_v4-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v1'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-x86_64-unknown-linux-musl-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v2'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-x86_64_v2-unknown-linux-musl-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v3'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-x86_64_v3-unknown-linux-musl-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v4'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-x86_64_v4-unknown-linux-musl-install_only.tar.gz', - ('windows', 'i386', 'msvc', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-i686-pc-windows-msvc-install_only.tar.gz', - ('windows', 'amd64', 'msvc', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-x86_64-pc-windows-msvc-install_only.tar.gz', - ('macos', 'arm64', '', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-aarch64-apple-darwin-install_only.tar.gz', - ('macos', 'x86_64', '', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.10.14%2B20240415-x86_64-apple-darwin-install_only.tar.gz', }, '3.9': { - ('linux', 'aarch64', 'gnu', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-aarch64-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'armv7', 'gnueabi', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-armv7-unknown-linux-gnueabi-install_only.tar.gz', - ('linux', 'armv7', 'gnueabihf', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-armv7-unknown-linux-gnueabihf-install_only.tar.gz', - ('linux', 'ppc64le', 'gnu', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-ppc64le-unknown-linux-gnu-install_only.tar.gz', - ('linux', 's390x', 'gnu', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-s390x-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'i686', 'gnu', ''): + ('linux', 'aarch64', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'armv7', 'gnueabi', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz', + ('linux', 'armv7', 'gnueabihf', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz', + ('linux', 'ppc64le', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 's390x', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-s390x-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v1', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v2', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v3', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v4', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v1', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-x86_64-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v2', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v3', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v4', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz', + ('windows', 'i386', 'msvc', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-i686-pc-windows-msvc-install_only_stripped.tar.gz', + ('windows', 'amd64', 'msvc', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-x86_64-pc-windows-msvc-install_only_stripped.tar.gz', + ('macos', 'arm64', '', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-aarch64-apple-darwin-install_only_stripped.tar.gz', + ('macos', 'x86_64', '', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241008/cpython-3.9.20%2B20241008-x86_64-apple-darwin-install_only_stripped.tar.gz', + ('linux', 'i686', 'gnu', '', ''): 'https://github.com/indygreg/python-build-standalone/releases/download/20230826/cpython-3.9.18%2B20230826-i686-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v1'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-x86_64-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v2'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-x86_64_v2-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v3'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-x86_64_v3-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v4'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-x86_64_v4-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v1'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-x86_64-unknown-linux-musl-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v2'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-x86_64_v2-unknown-linux-musl-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v3'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-x86_64_v3-unknown-linux-musl-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v4'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-x86_64_v4-unknown-linux-musl-install_only.tar.gz', - ('windows', 'i386', 'msvc', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-i686-pc-windows-msvc-install_only.tar.gz', - ('windows', 'amd64', 'msvc', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-x86_64-pc-windows-msvc-install_only.tar.gz', - ('macos', 'arm64', '', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-aarch64-apple-darwin-install_only.tar.gz', - ('macos', 'x86_64', '', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.9.19%2B20240415-x86_64-apple-darwin-install_only.tar.gz', }, '3.8': { - ('linux', 'aarch64', 'gnu', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.8.19%2B20240415-aarch64-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'i686', 'gnu', ''): + ('linux', 'aarch64', 'gnu', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241002/cpython-3.8.20%2B20241002-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'gnu', 'v1', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241002/cpython-3.8.20%2B20241002-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz', + ('linux', 'x86_64', 'musl', 'v1', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241002/cpython-3.8.20%2B20241002-x86_64-unknown-linux-musl-install_only_stripped.tar.gz', + ('windows', 'i386', 'msvc', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241002/cpython-3.8.20%2B20241002-i686-pc-windows-msvc-install_only_stripped.tar.gz', + ('windows', 'amd64', 'msvc', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241002/cpython-3.8.20%2B20241002-x86_64-pc-windows-msvc-install_only_stripped.tar.gz', + ('macos', 'arm64', '', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241002/cpython-3.8.20%2B20241002-aarch64-apple-darwin-install_only_stripped.tar.gz', + ('macos', 'x86_64', '', '', ''): + 'https://github.com/indygreg/python-build-standalone/releases/download/20241002/cpython-3.8.20%2B20241002-x86_64-apple-darwin-install_only_stripped.tar.gz', + ('linux', 'i686', 'gnu', '', ''): 'https://github.com/indygreg/python-build-standalone/releases/download/20230826/cpython-3.8.17%2B20230826-i686-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'gnu', 'v1'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.8.19%2B20240415-x86_64-unknown-linux-gnu-install_only.tar.gz', - ('linux', 'x86_64', 'musl', 'v1'): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.8.19%2B20240415-x86_64-unknown-linux-musl-install_only.tar.gz', - ('windows', 'i386', 'msvc', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.8.19%2B20240415-i686-pc-windows-msvc-install_only.tar.gz', - ('windows', 'amd64', 'msvc', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.8.19%2B20240415-x86_64-pc-windows-msvc-install_only.tar.gz', - ('macos', 'arm64', '', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.8.19%2B20240415-aarch64-apple-darwin-install_only.tar.gz', - ('macos', 'x86_64', '', ''): - 'https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.8.19%2B20240415-x86_64-apple-darwin-install_only.tar.gz', }, '3.7': { - ('linux', 'x86_64', 'gnu', ''): + ('linux', 'x86_64', 'gnu', '', ''): 'https://github.com/indygreg/python-build-standalone/releases/download/20200822/cpython-3.7.9-x86_64-unknown-linux-gnu-pgo-20200823T0036.tar.zst', - ('linux', 'x86_64', 'musl', ''): + ('linux', 'x86_64', 'musl', '', ''): 'https://github.com/indygreg/python-build-standalone/releases/download/20200822/cpython-3.7.9-x86_64-unknown-linux-musl-noopt-20200823T0036.tar.zst', - ('windows', 'i386', 'msvc', ''): + ('windows', 'i386', 'msvc', '', ''): 'https://github.com/indygreg/python-build-standalone/releases/download/20200822/cpython-3.7.9-i686-pc-windows-msvc-shared-pgo-20200823T0159.tar.zst', - ('windows', 'amd64', 'msvc', ''): + ('windows', 'amd64', 'msvc', '', ''): 'https://github.com/indygreg/python-build-standalone/releases/download/20200822/cpython-3.7.9-x86_64-pc-windows-msvc-shared-pgo-20200823T0118.tar.zst', - ('macos', 'x86_64', '', ''): + ('macos', 'x86_64', '', '', ''): 'https://github.com/indygreg/python-build-standalone/releases/download/20200823/cpython-3.7.9-x86_64-apple-darwin-pgo-20200823T2228.tar.zst', }, 'pypy3.10': { - ('linux', 'aarch64', 'gnu', ''): + ('linux', 'aarch64', 'gnu', '', ''): 'https://downloads.python.org/pypy/pypy3.10-v7.3.15-aarch64.tar.bz2', - ('linux', 'x86_64', 'gnu', ''): + ('linux', 'x86_64', 'gnu', '', ''): 'https://downloads.python.org/pypy/pypy3.10-v7.3.15-linux64.tar.bz2', - ('windows', 'amd64', 'msvc', ''): + ('windows', 'amd64', 'msvc', '', ''): 'https://downloads.python.org/pypy/pypy3.10-v7.3.15-win64.zip', - ('macos', 'arm64', '', ''): + ('macos', 'arm64', '', '', ''): 'https://downloads.python.org/pypy/pypy3.10-v7.3.15-macos_arm64.tar.bz2', - ('macos', 'x86_64', '', ''): + ('macos', 'x86_64', '', '', ''): 'https://downloads.python.org/pypy/pypy3.10-v7.3.15-macos_x86_64.tar.bz2', }, 'pypy3.9': { - ('linux', 'aarch64', 'gnu', ''): + ('linux', 'aarch64', 'gnu', '', ''): 'https://downloads.python.org/pypy/pypy3.9-v7.3.15-aarch64.tar.bz2', - ('linux', 'x86_64', 'gnu', ''): + ('linux', 'x86_64', 'gnu', '', ''): 'https://downloads.python.org/pypy/pypy3.9-v7.3.15-linux64.tar.bz2', - ('windows', 'amd64', 'msvc', ''): + ('windows', 'amd64', 'msvc', '', ''): 'https://downloads.python.org/pypy/pypy3.9-v7.3.15-win64.zip', - ('macos', 'arm64', '', ''): + ('macos', 'arm64', '', '', ''): 'https://downloads.python.org/pypy/pypy3.9-v7.3.15-macos_arm64.tar.bz2', - ('macos', 'x86_64', '', ''): + ('macos', 'x86_64', '', '', ''): 'https://downloads.python.org/pypy/pypy3.9-v7.3.15-macos_x86_64.tar.bz2', }, 'pypy2.7': { - ('linux', 'aarch64', 'gnu', ''): + ('linux', 'aarch64', 'gnu', '', ''): 'https://downloads.python.org/pypy/pypy2.7-v7.3.15-aarch64.tar.bz2', - ('linux', 'x86_64', 'gnu', ''): + ('linux', 'x86_64', 'gnu', '', ''): 'https://downloads.python.org/pypy/pypy2.7-v7.3.15-linux64.tar.bz2', - ('windows', 'amd64', 'msvc', ''): + ('windows', 'amd64', 'msvc', '', ''): 'https://downloads.python.org/pypy/pypy2.7-v7.3.15-win64.zip', - ('macos', 'arm64', '', ''): + ('macos', 'arm64', '', '', ''): 'https://downloads.python.org/pypy/pypy2.7-v7.3.15-macos_arm64.tar.bz2', - ('macos', 'x86_64', '', ''): + ('macos', 'x86_64', '', '', ''): 'https://downloads.python.org/pypy/pypy2.7-v7.3.15-macos_x86_64.tar.bz2', }, } diff --git a/src/hatch/python/resolve.py b/src/hatch/python/resolve.py index f25002cdb..d893e6152 100644 --- a/src/hatch/python/resolve.py +++ b/src/hatch/python/resolve.py @@ -165,7 +165,7 @@ def python_path(self) -> str: return f'{directory}/bin/pypy' -def get_distribution(name: str, source: str = '', variant: str = '') -> Distribution: +def get_distribution(name: str, source: str = '', variant_cpu: str = '', variant_gil: str = '') -> Distribution: if source: return _get_distribution_class(source)(name, source) @@ -184,14 +184,17 @@ def get_distribution(name: str, source: str = '', variant: str = '') -> Distribu system = 'linux' abi = 'gnu' if any(platform.libc_ver()) else 'musl' - if not variant: - variant = _get_default_variant(name, system, arch) + if not variant_cpu: + variant_cpu = _get_default_variant_cpu(name, system, arch) - key = (system, arch, abi, variant) + if not variant_gil: + variant_gil = _get_default_variant_gil() + + key = (system, arch, abi, variant_cpu, variant_gil) keys: dict[tuple, str] = DISTRIBUTIONS[name] if key not in keys: - message = f'Could not find a default source for {name=} {system=} {arch=} {abi=} {variant=}' + message = f'Could not find a default source for {name=} {system=} {arch=} {abi=} {variant_cpu=} {variant_gil=}' raise PythonDistributionResolutionError(message) source = keys[key] @@ -211,7 +214,7 @@ def get_compatible_distributions() -> dict[str, Distribution]: return distributions -def _guess_linux_variant() -> str: +def _guess_linux_variant_cpu() -> str: # Use the highest that we know is most common when we can't parse CPU data default = 'v3' try: @@ -248,12 +251,16 @@ def _guess_linux_variant() -> str: return default -def _get_default_variant(name: str, system: str, arch: str) -> str: +def _get_default_variant_cpu(name: str, system: str, arch: str) -> str: # not PyPy if name[0].isdigit(): - # https://gregoryszorc.com/docs/python-build-standalone/main/running.html - variant = os.environ.get(f'HATCH_PYTHON_VARIANT_{system.upper()}', '').lower() + variant = os.environ.get( + 'HATCH_PYTHON_VARIANT_CPU', + # Legacy name + os.environ.get(f'HATCH_PYTHON_VARIANT_{system.upper()}', ''), + ).lower() + # https://gregoryszorc.com/docs/python-build-standalone/main/running.html if system == 'linux' and arch == 'x86_64': # Intel-specific optimizations depending on age of release if variant: @@ -263,11 +270,15 @@ def _get_default_variant(name: str, system: str, arch: str) -> str: return 'v1' if name != '3.7': - return _guess_linux_variant() + return _guess_linux_variant_cpu() return '' +def _get_default_variant_gil() -> str: + return os.environ.get('HATCH_PYTHON_VARIANT_GIL', '').lower() + + def _get_distribution_class(source: str) -> type[Distribution]: if source.startswith('https://github.com/indygreg/python-build-standalone/releases/download/'): return CPythonStandaloneDistribution diff --git a/src/hatch/template/files_feature_ci.py b/src/hatch/template/files_feature_ci.py index 8327f7640..d7dfee9d9 100644 --- a/src/hatch/template/files_feature_ci.py +++ b/src/hatch/template/files_feature_ci.py @@ -28,7 +28,7 @@ class CommandLinePackage(File): fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] steps: - uses: actions/checkout@v3 diff --git a/tests/cli/env/test_show.py b/tests/cli/env/test_show.py index e7c3e0956..09edc8286 100644 --- a/tests/cli/env/test_show.py +++ b/tests/cli/env/test_show.py @@ -71,6 +71,7 @@ def test_default_as_json(hatch, temp_dir, config_file): 'default', 'hatch-build', 'hatch-static-analysis', + 'hatch-test.py3.13', 'hatch-test.py3.12', 'hatch-test.py3.11', 'hatch-test.py3.10', diff --git a/tests/helpers/templates/new/feature_ci.py b/tests/helpers/templates/new/feature_ci.py index 0ce2a06b3..519371cff 100644 --- a/tests/helpers/templates/new/feature_ci.py +++ b/tests/helpers/templates/new/feature_ci.py @@ -34,7 +34,7 @@ def get_files(**kwargs): fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] steps: - uses: actions/checkout@v3 diff --git a/tests/python/test_resolve.py b/tests/python/test_resolve.py index 7af955cf7..394d324ad 100644 --- a/tests/python/test_resolve.py +++ b/tests/python/test_resolve.py @@ -19,7 +19,7 @@ def test_unknown_distribution(self): reason='No variants for this platform and architecture combination', ) def test_resolution_error(self, platform): - with EnvVars({f'HATCH_PYTHON_VARIANT_{platform.name.upper()}': 'foo'}), pytest.raises( + with EnvVars({'HATCH_PYTHON_VARIANT_CPU': 'foo'}), pytest.raises( PythonDistributionResolutionError, match=f"Could not find a default source for name='3.11' system='{platform.name}' arch=", ): @@ -76,68 +76,83 @@ def test_pypy_custom(self): assert dist.python_path == 'foo/bar/python' -@pytest.mark.parametrize( - ('system', 'variant'), - [ - ('linux', 'v1'), - ('linux', 'v2'), - ('linux', 'v3'), - ('linux', 'v4'), - ], -) -def test_variants(platform, system, variant, current_arch): - if platform.name != system: - pytest.skip(f'Skipping test for: {system}') - - with EnvVars({f'HATCH_PYTHON_VARIANT_{system.upper()}': variant}): - dist = get_distribution('3.11') - - if system == 'linux' and (current_arch != 'x86_64' or variant == 'v1'): - assert variant not in dist.source - else: - assert variant in dist.source - - -@pytest.mark.skipif( - not (sys.platform == 'linux' and machine().lower() == 'x86_64'), - reason='No variants for this platform and architecture combination', -) -@pytest.mark.parametrize( - ('variant', 'flags'), - [ - pytest.param( - 'v1', - # Just guessing here... - 'flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni dtes64 monitor ds_cpl smx est tm2', - id='v1', - ), - pytest.param( - 'v2', - # Intel Core i7-860 - 'flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni dtes64 monitor ds_cpl smx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm pti ssbd ibrs ibpb stibp dtherm ida flush_l1d', - id='v2', - ), - pytest.param( - 'v3', - # Intel Core i5-5300U - 'flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap intel_pt xsaveopt dtherm ida arat pln pts vnmi md_clear flush_l1d', - id='v3', - ), - pytest.param( - 'v4', - # Just guessing here... - 'flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap intel_pt xsaveopt dtherm ida arat pln pts vnmi md_clear flush_l1d avx512f avx512bw avx512cd avx512dq avx512vl', - id='v4', - ), - ], -) -def test_guess_variant(fs, variant, flags): - fs.create_file('/proc/cpuinfo', contents=flags) - - with EnvVars({'HATCH_PYTHON_VARIANT_LINUX': ''}): - dist = get_distribution('3.11') - if variant == 'v1': - for v in ('v1', 'v2', 'v3', 'v4'): - assert v not in dist.source +@pytest.mark.requires_linux +class TestVariantCPU: + def test_legacy_option(self, current_arch): + variant = 'v4' + with EnvVars({'HATCH_PYTHON_VARIANT_LINUX': variant}): + dist = get_distribution('3.12') + + if current_arch != 'x86_64': + assert variant not in dist.source + else: + assert variant in dist.source + + @pytest.mark.parametrize('variant', ['v1', 'v2', 'v3', 'v4']) + def test_compatibility(self, variant, current_arch): + with EnvVars({'HATCH_PYTHON_VARIANT_CPU': variant}): + dist = get_distribution('3.12') + + if current_arch != 'x86_64' or variant == 'v1': + assert variant not in dist.source else: assert variant in dist.source + + @pytest.mark.skipif( + machine().lower() != 'x86_64', + reason='No variants for this platform and architecture combination', + ) + @pytest.mark.parametrize( + ('variant', 'flags'), + [ + pytest.param( + 'v1', + # Just guessing here... + 'flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni dtes64 monitor ds_cpl smx est tm2', + id='v1', + ), + pytest.param( + 'v2', + # Intel Core i7-860 + 'flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni dtes64 monitor ds_cpl smx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm pti ssbd ibrs ibpb stibp dtherm ida flush_l1d', + id='v2', + ), + pytest.param( + 'v3', + # Intel Core i5-5300U + 'flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap intel_pt xsaveopt dtherm ida arat pln pts vnmi md_clear flush_l1d', + id='v3', + ), + pytest.param( + 'v4', + # Just guessing here... + 'flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap intel_pt xsaveopt dtherm ida arat pln pts vnmi md_clear flush_l1d avx512f avx512bw avx512cd avx512dq avx512vl', + id='v4', + ), + ], + ) + def test_guess_variant(self, fs, variant, flags): + fs.create_file('/proc/cpuinfo', contents=flags) + + with EnvVars({'HATCH_PYTHON_VARIANT_CPU': ''}): + dist = get_distribution('3.12') + if variant == 'v1': + for v in ('v1', 'v2', 'v3', 'v4'): + assert v not in dist.source + else: + assert variant in dist.source + + +class TestVariantGIL: + def test_compatible(self): + with EnvVars({'HATCH_PYTHON_VARIANT_GIL': 'freethreaded'}): + dist = get_distribution('3.13') + + assert 'freethreaded' in dist.source + + def test_incompatible(self, platform): + with EnvVars({'HATCH_PYTHON_VARIANT_GIL': 'freethreaded'}), pytest.raises( + PythonDistributionResolutionError, + match=f"Could not find a default source for name='3.12' system='{platform.name}' arch=", + ): + get_distribution('3.12') From c70a1781705eb86b4ee9e4b3c1fb834ab93b44eb Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Mon, 14 Oct 2024 00:21:36 -0400 Subject: [PATCH 03/10] Improve distribution variant config docs --- docs/plugins/environment/virtual.md | 2 +- docs/tutorials/python/manage.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/environment/virtual.md b/docs/plugins/environment/virtual.md index a20ebd6bc..0e36fcb49 100644 --- a/docs/plugins/environment/virtual.md +++ b/docs/plugins/environment/virtual.md @@ -72,7 +72,7 @@ The following options are recognized for internal Python resolution. The source of distributions is the [python-build-standalone](https://github.com/indygreg/python-build-standalone) project. -Some distributions have [variants](https://gregoryszorc.com/docs/python-build-standalone/main/running.html) that may be configured. Options may be combined. +Some distributions have [variants](https://gregoryszorc.com/docs/python-build-standalone/main/running.html) that may be configured with environment variables. Options may be combined. | Option | Platforms | Allowed values | | --- | --- | --- | diff --git a/docs/tutorials/python/manage.md b/docs/tutorials/python/manage.md index 43ca6c650..1ba0f5b6f 100644 --- a/docs/tutorials/python/manage.md +++ b/docs/tutorials/python/manage.md @@ -126,7 +126,7 @@ When there are no updates available for a distribution, a warning will be displa ``` $ hatch python update 3.12 -The latest version is already installed: 3.12.3 +The latest version is already installed: 3.12.7 ``` ## Removal From 9650a1dd3b606cf4777f42bd95874c2a9be0d6a2 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Mon, 21 Oct 2024 01:46:49 +0100 Subject: [PATCH 04/10] Fix failing test due to unsorted iterdir (#1761) --- tests/cli/env/test_create.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/cli/env/test_create.py b/tests/cli/env/test_create.py index 7106f4479..f5de70c02 100644 --- a/tests/cli/env/test_create.py +++ b/tests/cli/env/test_create.py @@ -1560,13 +1560,11 @@ def update(self, metadata): storage_path = storage_dirs[0] assert len(storage_path.name) == 8 - env_dirs = list(storage_path.iterdir()) - assert len(env_dirs) == 2 + env_dirs = sorted(storage_path.iterdir()) + assert [d.name for d in env_dirs] == ['hatch-build', 'test'] env_path = env_dirs[1] - assert env_path.name == 'test' - with UVVirtualEnv(env_path, platform): output = platform.run_command([uv_on_path, 'pip', 'freeze'], check=True, capture_output=True).stdout.decode( 'utf-8' From 750b4984ef13b205e7d0624baab2ad589e9d32bd Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Fri, 8 Nov 2024 00:39:08 +0100 Subject: [PATCH 05/10] Upgrade Ruff to 0.6.8 (#1588) Co-authored-by: Ofek Lev --- backend/src/hatchling/metadata/spec.py | 2 +- docs/.hooks/render_ruff_defaults.py | 1 + ruff_defaults.toml | 12 +- src/hatch/cli/fmt/core.py | 131 +++++++++++++--------- src/hatch/env/internal/static_analysis.py | 2 +- 5 files changed, 85 insertions(+), 63 deletions(-) diff --git a/backend/src/hatchling/metadata/spec.py b/backend/src/hatchling/metadata/spec.py index 16cb6d1c5..a83ea3db1 100644 --- a/backend/src/hatchling/metadata/spec.py +++ b/backend/src/hatchling/metadata/spec.py @@ -174,7 +174,7 @@ def project_metadata_from_core_metadata(core_metadata: str) -> dict[str, Any]: left, _, right = marker if left.value == 'extra': extra = right.value - del markers[i] + del markers[i] # noqa: B909 # If there was only one marker then there will be an unnecessary # trailing semicolon in the string representation if not markers: diff --git a/docs/.hooks/render_ruff_defaults.py b/docs/.hooks/render_ruff_defaults.py index e6a73c373..8f4d0fdaf 100644 --- a/docs/.hooks/render_ruff_defaults.py +++ b/docs/.hooks/render_ruff_defaults.py @@ -270,6 +270,7 @@ def run(self, lines): # noqa: PLR6301 'PLR0915', 'PLR0916', 'PLR0917', + 'PLR1701', 'PLR1702', 'PLR1706', 'PT004', diff --git a/ruff_defaults.toml b/ruff_defaults.toml index 698e8e5a0..9acc585a3 100644 --- a/ruff_defaults.toml +++ b/ruff_defaults.toml @@ -10,13 +10,12 @@ select = [ "A002", "A003", "ARG001", + "ARG001", "ARG002", "ARG003", "ARG004", "ARG005", "ASYNC100", - "ASYNC101", - "ASYNC102", "B002", "B003", "B004", @@ -130,7 +129,6 @@ select = [ "E742", "E743", "E902", - "E999", "EM101", "EM102", "EM103", @@ -323,7 +321,6 @@ select = [ "PLR0203", "PLR0206", "PLR0402", - "PLR1701", "PLR1704", "PLR1711", "PLR1714", @@ -466,7 +463,6 @@ select = [ "RUF022", "RUF023", "RUF024", - "RUF025", "RUF026", "RUF027", "RUF028", @@ -590,11 +586,6 @@ select = [ "TID251", "TID252", "TID253", - "TRIO100", - "TRIO105", - "TRIO109", - "TRIO110", - "TRIO115", "TRY002", "TRY003", "TRY004", @@ -628,7 +619,6 @@ select = [ "UP024", "UP025", "UP026", - "UP027", "UP028", "UP029", "UP030", diff --git a/src/hatch/cli/fmt/core.py b/src/hatch/cli/fmt/core.py index 8b079d89a..de454a342 100644 --- a/src/hatch/cli/fmt/core.py +++ b/src/hatch/cli/fmt/core.py @@ -167,8 +167,16 @@ def get_config(self, section: str) -> dict[str, Any]: 'ARG004', 'ARG005', 'ASYNC100', - 'ASYNC101', - 'ASYNC102', + 'ASYNC105', + 'ASYNC109', + 'ASYNC110', + 'ASYNC115', + 'ASYNC210', + 'ASYNC220', + 'ASYNC221', + 'ASYNC222', + 'ASYNC230', + 'ASYNC251', 'B002', 'B003', 'B004', @@ -307,6 +315,17 @@ def get_config(self, section: str) -> dict[str, Any]: 'FBT001', 'FBT002', 'FLY002', + 'FURB105', + 'FURB129', + 'FURB136', + 'FURB161', + 'FURB163', + 'FURB167', + 'FURB168', + 'FURB169', + 'FURB177', + 'FURB181', + 'FURB187', 'G001', 'G002', 'G003', @@ -349,6 +368,7 @@ def get_config(self, section: str) -> dict[str, Any]: 'PERF102', 'PERF401', 'PERF402', + 'PERF403', 'PGH005', 'PIE790', 'PIE794', @@ -364,18 +384,28 @@ def get_config(self, section: str) -> dict[str, Any]: 'PLC0205', 'PLC0208', 'PLC0414', + 'PLC2401', + 'PLC2403', 'PLC3002', 'PLE0100', 'PLE0101', + 'PLE0115', 'PLE0116', 'PLE0117', 'PLE0118', 'PLE0237', 'PLE0241', 'PLE0302', + 'PLE0303', + 'PLE0305', 'PLE0307', + 'PLE0308', + 'PLE0309', 'PLE0604', 'PLE0605', + 'PLE0643', + 'PLE0704', + 'PLE1132', 'PLE1142', 'PLE1205', 'PLE1206', @@ -383,6 +413,8 @@ def get_config(self, section: str) -> dict[str, Any]: 'PLE1307', 'PLE1310', 'PLE1507', + 'PLE1519', + 'PLE1520', 'PLE1700', 'PLE2502', 'PLE2510', @@ -394,23 +426,34 @@ def get_config(self, section: str) -> dict[str, Any]: 'PLR0133', 'PLR0206', 'PLR0402', - 'PLR1701', + 'PLR1704', 'PLR1711', 'PLR1714', 'PLR1722', + 'PLR1730', + 'PLR1736', 'PLR2004', + 'PLR2044', 'PLR5501', 'PLW0120', 'PLW0127', + 'PLW0128', 'PLW0129', 'PLW0131', + 'PLW0133', + 'PLW0211', + 'PLW0245', 'PLW0406', 'PLW0602', 'PLW0603', + 'PLW0604', + 'PLW0642', 'PLW0711', + 'PLW1501', 'PLW1508', 'PLW1509', 'PLW1510', + 'PLW2101', 'PLW2901', 'PLW3301', 'PT001', @@ -485,7 +528,9 @@ def get_config(self, section: str) -> dict[str, Any]: 'PYI054', 'PYI055', 'PYI056', + 'PYI057', 'PYI058', + 'PYI062', 'RET503', 'RET504', 'RET505', @@ -510,7 +555,10 @@ def get_config(self, section: str) -> dict[str, Any]: 'RUF018', 'RUF019', 'RUF020', + 'RUF024', + 'RUF026', 'RUF100', + 'RUF101', 'S101', 'S102', 'S103', @@ -563,6 +611,7 @@ def get_config(self, section: str) -> dict[str, Any]: 'S607', 'S608', 'S609', + 'S610', 'S611', 'S612', 'S701', @@ -615,11 +664,6 @@ def get_config(self, section: str) -> dict[str, Any]: 'TID251', 'TID252', 'TID253', - 'TRIO100', - 'TRIO105', - 'TRIO109', - 'TRIO110', - 'TRIO115', 'TRY002', 'TRY003', 'TRY004', @@ -685,7 +729,20 @@ def get_config(self, section: str) -> dict[str, Any]: 'YTT303', ) PREVIEW_RULES: tuple[str, ...] = ( + 'A004', + 'A005', + 'A006', + 'ASYNC116', + 'B039', + 'B901', 'B909', + 'C420', + 'DOC201', + 'DOC202', + 'DOC402', + 'DOC403', + 'DOC501', + 'DOC502', 'E112', 'E113', 'E115', @@ -693,6 +750,7 @@ def get_config(self, section: str) -> dict[str, Any]: 'E201', 'E202', 'E203', + 'E204', 'E211', 'E221', 'E222', @@ -717,88 +775,61 @@ def get_config(self, section: str) -> dict[str, Any]: 'E274', 'E275', 'E502', - 'FURB105', + 'FAST001', + 'FAST002', + 'FAST003', 'FURB110', 'FURB113', 'FURB116', 'FURB118', - 'FURB129', 'FURB131', 'FURB132', - 'FURB136', 'FURB142', 'FURB145', 'FURB148', 'FURB152', + 'FURB154', 'FURB157', - 'FURB161', - 'FURB163', 'FURB164', 'FURB166', - 'FURB167', - 'FURB168', - 'FURB169', 'FURB171', - 'FURB177', 'FURB180', - 'FURB181', - 'FURB187', + 'FURB188', 'FURB192', - 'PERF403', + 'PLC0206', 'PLC0415', 'PLC1901', - 'PLC2401', - 'PLC2403', 'PLC2701', 'PLC2801', - 'PLE0115', - 'PLE0303', 'PLE0304', - 'PLE0305', - 'PLE0308', - 'PLE0309', - 'PLE0643', - 'PLE0704', - 'PLE1132', 'PLE1141', - 'PLE1519', - 'PLE1520', 'PLE4703', 'PLR0202', 'PLR0203', - 'PLR1704', - 'PLR1730', 'PLR1733', - 'PLR1736', - 'PLR2044', 'PLR6104', 'PLR6201', 'PLR6301', 'PLW0108', - 'PLW0128', - 'PLW0133', 'PLW0177', - 'PLW0211', - 'PLW0245', - 'PLW0604', - 'PLW0642', - 'PLW1501', 'PLW1514', 'PLW1641', - 'PLW2101', 'PLW3201', 'PYI059', - 'PYI062', + 'PYI063', + 'PYI064', + 'PYI066', 'RUF021', 'RUF022', 'RUF023', - 'RUF024', - 'RUF025', - 'RUF026', 'RUF027', 'RUF028', 'RUF029', - 'RUF101', + 'RUF030', + 'RUF031', + 'RUF032', + 'RUF033', + 'RUF034', 'S401', 'S402', 'S403', @@ -811,8 +842,8 @@ def get_config(self, section: str) -> dict[str, Any]: 'S412', 'S413', 'S415', - 'S610', 'UP042', + 'UP043', 'W391', ) PER_FILE_IGNORED_RULES: dict[str, list[str]] = { diff --git a/src/hatch/env/internal/static_analysis.py b/src/hatch/env/internal/static_analysis.py index c54895a8f..c4bd01a42 100644 --- a/src/hatch/env/internal/static_analysis.py +++ b/src/hatch/env/internal/static_analysis.py @@ -17,4 +17,4 @@ def get_default_config() -> dict[str, Any]: } -RUFF_DEFAULT_VERSION: str = '0.4.5' +RUFF_DEFAULT_VERSION: str = '0.6.8' From a2bcbcbadb5152d78bfb471a77b33f5c3aa0aefc Mon Sep 17 00:00:00 2001 From: Leah Wasser Date: Thu, 7 Nov 2024 16:41:51 -0700 Subject: [PATCH 06/10] feat(docs): contributing file update & envt fix (#1779) --- docs/community/contributing.md | 2 +- hatch.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/community/contributing.md b/docs/community/contributing.md index 88ad71b5d..40dfa2813 100644 --- a/docs/community/contributing.md +++ b/docs/community/contributing.md @@ -65,4 +65,4 @@ Build and validate the documentation website: ```bash hatch run docs:build-check -``` +``` \ No newline at end of file diff --git a/hatch.toml b/hatch.toml index 51b950c92..695204d3c 100644 --- a/hatch.toml +++ b/hatch.toml @@ -65,6 +65,7 @@ dependencies = [ # Validation # https://github.com/linkchecker/linkchecker/pull/669#issuecomment-1267236287 "linkchecker @ git+https://github.com/linkchecker/linkchecker.git@d9265bb71c2054bf57b8c5734a4825d62505c779", + "griffe<1.0", ] pre-install-commands = [ "python scripts/install_mkdocs_material_insiders.py", From 3d312c788951a6a7c5760b155a647800c5f56c4d Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sat, 9 Nov 2024 11:17:23 -0500 Subject: [PATCH 07/10] Fix CI (#1789) --- .github/workflows/build-distributions.yml | 2 +- .github/workflows/build-hatch.yml | 8 ++++---- .github/workflows/build-hatchling.yml | 2 +- .github/workflows/cli.yml | 2 +- .github/workflows/docs-dev.yml | 2 +- .github/workflows/docs-release.yml | 2 +- .github/workflows/test.yml | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-distributions.yml b/.github/workflows/build-distributions.yml index aac9c4d8e..542031b4e 100644 --- a/.github/workflows/build-distributions.yml +++ b/.github/workflows/build-distributions.yml @@ -32,7 +32,7 @@ jobs: python-version: ${{ env.DIST_PYTHON_VERSION }} - name: Install UV - run: curl -LsSf https://github.com/astral-sh/uv/releases/latest/download/uv-installer.sh | sh + uses: astral-sh/setup-uv@v3 - name: Install Hatch if: inputs.version diff --git a/.github/workflows/build-hatch.yml b/.github/workflows/build-hatch.yml index 7645d0bec..d37a8ea43 100644 --- a/.github/workflows/build-hatch.yml +++ b/.github/workflows/build-hatch.yml @@ -45,7 +45,7 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} - name: Install UV - run: curl -LsSf https://github.com/astral-sh/uv/releases/latest/download/uv-installer.sh | sh + uses: astral-sh/setup-uv@v3 - name: Install tools run: uv pip install --system build hatch @@ -158,7 +158,7 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} - name: Install UV - run: curl -LsSf https://github.com/astral-sh/uv/releases/latest/download/uv-installer.sh | sh + uses: astral-sh/setup-uv@v3 - name: Install Hatch run: |- @@ -284,7 +284,7 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} - name: Install UV - run: curl -LsSf https://github.com/astral-sh/uv/releases/latest/download/uv-installer.sh | sh + uses: astral-sh/setup-uv@v3 - name: Install PyOxidizer ${{ env.PYOXIDIZER_VERSION }} run: uv pip install --system pyoxidizer==${{ env.PYOXIDIZER_VERSION }} @@ -370,7 +370,7 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} - name: Install UV - run: curl -LsSf https://github.com/astral-sh/uv/releases/latest/download/uv-installer.sh | sh + uses: astral-sh/setup-uv@v3 - name: Install PyOxidizer ${{ env.PYOXIDIZER_VERSION }} run: uv pip install --system pyoxidizer==${{ env.PYOXIDIZER_VERSION }} diff --git a/.github/workflows/build-hatchling.yml b/.github/workflows/build-hatchling.yml index 19b4c5bb1..803d13ec9 100644 --- a/.github/workflows/build-hatchling.yml +++ b/.github/workflows/build-hatchling.yml @@ -22,7 +22,7 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} - name: Install UV - run: curl -LsSf https://github.com/astral-sh/uv/releases/latest/download/uv-installer.sh | sh + uses: astral-sh/setup-uv@v3 - name: Install build dependencies run: uv pip install --system --upgrade build diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 9859eac61..e384a9de9 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -30,7 +30,7 @@ jobs: python-version: ${{ env.STABLE_PYTHON_VERSION }} - name: Install UV - run: curl -LsSf https://github.com/astral-sh/uv/releases/latest/download/uv-installer.sh | sh + uses: astral-sh/setup-uv@v3 - name: Install hyperfine uses: taiki-e/install-action@v2 diff --git a/.github/workflows/docs-dev.yml b/.github/workflows/docs-dev.yml index 4def6ab65..f8a3c72a0 100644 --- a/.github/workflows/docs-dev.yml +++ b/.github/workflows/docs-dev.yml @@ -33,7 +33,7 @@ jobs: run: python scripts/validate_history.py - name: Install UV - run: curl -LsSf https://github.com/astral-sh/uv/releases/latest/download/uv-installer.sh | sh + uses: astral-sh/setup-uv@v3 - name: Install ourself run: | diff --git a/.github/workflows/docs-release.yml b/.github/workflows/docs-release.yml index 594858936..54e6e59cd 100644 --- a/.github/workflows/docs-release.yml +++ b/.github/workflows/docs-release.yml @@ -31,7 +31,7 @@ jobs: run: python scripts/validate_history.py - name: Install UV - run: curl -LsSf https://github.com/astral-sh/uv/releases/latest/download/uv-installer.sh | sh + uses: astral-sh/setup-uv@v3 - name: Install ourself run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1e3052598..0d4b62408 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,8 +34,8 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: Install UV - run: curl -LsSf https://github.com/astral-sh/uv/releases/latest/download/uv-installer.sh | sh + - name: Install uv + uses: astral-sh/setup-uv@v3 - name: Install ourself run: | From f8a2eaa2e0ce80a931837539d8f565ceeab75961 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sat, 9 Nov 2024 11:35:16 -0500 Subject: [PATCH 08/10] Bump `packaging` to 24.2 (#1788) --- backend/pyproject.toml | 2 +- backend/scripts/update_licenses.py | 71 -- backend/src/hatchling/licenses/__init__.py | 0 backend/src/hatchling/licenses/parse.py | 93 --- backend/src/hatchling/licenses/supported.py | 713 -------------------- backend/src/hatchling/metadata/core.py | 4 +- docs/history/hatch.md | 1 + docs/history/hatchling.md | 4 + hatch.toml | 4 - pyproject.toml | 2 +- src/hatch/template/default.py | 2 +- tests/backend/licenses/__init__.py | 0 tests/backend/licenses/test_parse.py | 56 -- tests/backend/licenses/test_supported.py | 31 - tests/backend/metadata/test_core.py | 2 +- 15 files changed, 11 insertions(+), 974 deletions(-) delete mode 100644 backend/scripts/update_licenses.py delete mode 100644 backend/src/hatchling/licenses/__init__.py delete mode 100644 backend/src/hatchling/licenses/parse.py delete mode 100644 backend/src/hatchling/licenses/supported.py delete mode 100644 tests/backend/licenses/__init__.py delete mode 100644 tests/backend/licenses/test_parse.py delete mode 100644 tests/backend/licenses/test_supported.py diff --git a/backend/pyproject.toml b/backend/pyproject.toml index d90a6a262..722b57317 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -35,7 +35,7 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", ] dependencies = [ - "packaging>=23.2", + "packaging>=24.2", "pathspec>=0.10.1", "pluggy>=1.0.0", "tomli>=1.2.2; python_version < '3.11'", diff --git a/backend/scripts/update_licenses.py b/backend/scripts/update_licenses.py deleted file mode 100644 index a7820b43f..000000000 --- a/backend/scripts/update_licenses.py +++ /dev/null @@ -1,71 +0,0 @@ -import json -import pathlib -import time -from contextlib import closing -from io import StringIO - -import httpx - -LATEST_API = 'https://api.github.com/repos/spdx/license-list-data/releases/latest' -LICENSES_URL = 'https://raw.githubusercontent.com/spdx/license-list-data/v{}/json/licenses.json' -EXCEPTIONS_URL = 'https://raw.githubusercontent.com/spdx/license-list-data/v{}/json/exceptions.json' - - -def download_data(url): - for _ in range(600): - try: - response = httpx.get(url) - response.raise_for_status() - except Exception: # noqa: BLE001 - time.sleep(1) - continue - else: - return json.loads(response.content.decode('utf-8')) - - message = 'Download failed' - raise ConnectionError(message) - - -def main(): - latest_version = download_data(LATEST_API)['tag_name'][1:] - - licenses = {} - for license_data in download_data(LICENSES_URL.format(latest_version))['licenses']: - license_id = license_data['licenseId'] - deprecated = license_data['isDeprecatedLicenseId'] - licenses[license_id.lower()] = {'id': license_id, 'deprecated': deprecated} - - exceptions = {} - for exception_data in download_data(EXCEPTIONS_URL.format(latest_version))['exceptions']: - exception_id = exception_data['licenseExceptionId'] - deprecated = exception_data['isDeprecatedLicenseId'] - exceptions[exception_id.lower()] = {'id': exception_id, 'deprecated': deprecated} - - project_root = pathlib.Path(__file__).resolve().parent.parent - data_file = project_root / 'src' / 'hatchling' / 'licenses' / 'supported.py' - - with closing(StringIO()) as file_contents: - file_contents.write( - f"""\ -from __future__ import annotations - -VERSION = {latest_version!r}\n\nLICENSES: dict[str, dict[str, str | bool]] = {{ -""" - ) - - for normalized_name, data in sorted(licenses.items()): - file_contents.write(f' {normalized_name!r}: {data!r},\n') - - file_contents.write('}\n\nEXCEPTIONS: dict[str, dict[str, str | bool]] = {\n') - - for normalized_name, data in sorted(exceptions.items()): - file_contents.write(f' {normalized_name!r}: {data!r},\n') - - file_contents.write('}\n') - - with data_file.open('w', encoding='utf-8') as f: - f.write(file_contents.getvalue()) - - -if __name__ == '__main__': - main() diff --git a/backend/src/hatchling/licenses/__init__.py b/backend/src/hatchling/licenses/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/backend/src/hatchling/licenses/parse.py b/backend/src/hatchling/licenses/parse.py deleted file mode 100644 index 8597655c6..000000000 --- a/backend/src/hatchling/licenses/parse.py +++ /dev/null @@ -1,93 +0,0 @@ -from __future__ import annotations - -from typing import cast - -from hatchling.licenses.supported import EXCEPTIONS, LICENSES - - -def get_valid_licenses() -> dict[str, dict[str, str | bool]]: - valid_licenses = LICENSES.copy() - - # https://peps.python.org/pep-0639/#should-custom-license-identifiers-be-allowed - public_license = 'LicenseRef-Public-Domain' - valid_licenses[public_license.lower()] = {'id': public_license, 'deprecated': False} - - proprietary_license = 'LicenseRef-Proprietary' - valid_licenses[proprietary_license.lower()] = {'id': proprietary_license, 'deprecated': False} - - return valid_licenses - - -def normalize_license_expression(raw_license_expression: str) -> str: - if not raw_license_expression: - return raw_license_expression - - valid_licenses = get_valid_licenses() - - # First normalize to lower case so we can look up licenses/exceptions - # and so boolean operators are Python-compatible - license_expression = raw_license_expression.lower() - - # Then pad parentheses so tokenization can be achieved by merely splitting on white space - license_expression = license_expression.replace('(', ' ( ').replace(')', ' ) ') - - # Now we begin parsing - 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': # noqa: S105 - python_tokens.append('or') - elif token == '(' and python_tokens and python_tokens[-1] not in {'or', 'and'}: # noqa: S105 - message = f'invalid license expression: {raw_license_expression}' - raise ValueError(message) - else: - python_tokens.append(token) - - python_expression = ' '.join(python_tokens) - try: - result = eval(python_expression) # noqa: S307 - except Exception: # noqa: BLE001 - result = True - - if result is not False: - message = f'invalid license expression: {raw_license_expression}' - raise ValueError(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}' - raise ValueError(message) - - normalized_tokens.append(cast(str, EXCEPTIONS[token]['id'])) - else: - if token.endswith('+'): - final_token = token[:-1] - suffix = '+' - else: - final_token = token - suffix = '' - - if final_token not in valid_licenses: - message = f'unknown license: {final_token}' - raise ValueError(message) - - normalized_tokens.append(cast(str, valid_licenses[final_token]['id']) + suffix) - - # Construct the normalized expression - normalized_expression = ' '.join(normalized_tokens) - - # Fix internal padding for parentheses - return normalized_expression.replace('( ', '(').replace(' )', ')') diff --git a/backend/src/hatchling/licenses/supported.py b/backend/src/hatchling/licenses/supported.py deleted file mode 100644 index 43de8636c..000000000 --- a/backend/src/hatchling/licenses/supported.py +++ /dev/null @@ -1,713 +0,0 @@ -from __future__ import annotations - -VERSION = '3.23' - -LICENSES: dict[str, dict[str, str | bool]] = { - '0bsd': {'id': '0BSD', '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}, - '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}, - '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-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}, - '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}, - '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}, - '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}, - 'haskellreport': {'id': 'HaskellReport', 'deprecated': False}, - 'hdparm': {'id': 'hdparm', '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-modify': {'id': 'HPND-export-US-modify', 'deprecated': False}, - 'hpnd-fenneberg-livingston': {'id': 'HPND-Fenneberg-Livingston', 'deprecated': False}, - 'hpnd-inria-imag': {'id': 'HPND-INRIA-IMAG', 'deprecated': False}, - 'hpnd-kevlin-henney': {'id': 'HPND-Kevlin-Henney', 'deprecated': False}, - 'hpnd-markus-kuhn': {'id': 'HPND-Markus-Kuhn', 'deprecated': False}, - 'hpnd-mit-disclaimer': {'id': 'HPND-MIT-disclaimer', '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-uc': {'id': 'HPND-UC', '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-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}, - 'ncgl-uk-2.0': {'id': 'NCGL-UK-2.0', 'deprecated': False}, - 'ncsa': {'id': 'NCSA', 'deprecated': False}, - 'net-snmp': {'id': 'Net-SNMP', 'deprecated': False}, - '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}, - '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}, - '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}, - '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}, - '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}, - '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}, - '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}, - '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}, - '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}, - '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, dict[str, str | bool]] = { - '389-exception': {'id': '389-exception', 'deprecated': False}, - 'asterisk-exception': {'id': 'Asterisk-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}, - '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}, - '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}, - '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/backend/src/hatchling/metadata/core.py b/backend/src/hatchling/metadata/core.py index e4b47a8d7..97359325d 100644 --- a/backend/src/hatchling/metadata/core.py +++ b/backend/src/hatchling/metadata/core.py @@ -674,10 +674,10 @@ def license(self) -> str: self._license = '' self._license_expression = '' elif isinstance(data, str): - from hatchling.licenses.parse import normalize_license_expression + from packaging.licenses import canonicalize_license_expression try: - self._license_expression = normalize_license_expression(data) + self._license_expression = str(canonicalize_license_expression(data)) except ValueError as e: message = f'Error parsing field `project.license` - {e}' raise ValueError(message) from None diff --git a/docs/history/hatch.md b/docs/history/hatch.md index 486d1fb61..e8988d7c5 100644 --- a/docs/history/hatch.md +++ b/docs/history/hatch.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - The `version` command accepts a `--force` option, allowing for downgrades when an explicit version number is given. - Build environments can now be configured, the default build environment is `hatch-build` - The environment interface now has the following methods and properties in order to better support builds on remote machines: `project_root`, `sep`, `pathsep`, `fs_context` +- Bump the minimum supported version of `packaging` to 24.2 ## [1.13.0](https://github.com/pypa/hatch/releases/tag/hatch-v1.13.0) - 2024-10-13 ## {: #hatch-v1.13.0 } diff --git a/docs/history/hatchling.md b/docs/history/hatchling.md index 363e7d787..eff824151 100644 --- a/docs/history/hatchling.md +++ b/docs/history/hatchling.md @@ -8,6 +8,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## Unreleased +***Added:*** + +- Bump the minimum supported version of `packaging` to 24.2 + ## [1.25.0](https://github.com/pypa/hatch/releases/tag/hatchling-v1.25.0) - 2024-06-22 ## {: #hatchling-v1.25.0 } ***Changed:*** diff --git a/hatch.toml b/hatch.toml index 695204d3c..766ee1ffb 100644 --- a/hatch.toml +++ b/hatch.toml @@ -117,11 +117,7 @@ update-hatch = [ "update-distributions", "update-ruff", ] -update-hatchling = [ - "update-licenses", -] update-distributions = "python scripts/update_distributions.py" -update-licenses = "python backend/scripts/update_licenses.py" update-ruff = [ "{env:HATCH_UV} pip install --upgrade ruff", "python scripts/update_ruff.py", diff --git a/pyproject.toml b/pyproject.toml index 4fd7e3da8..66b1c6306 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ dependencies = [ "httpx>=0.22.0", "hyperlink>=21.0.0", "keyring>=23.5.0", - "packaging>=23.2", + "packaging>=24.2", "pexpect~=4.8", "platformdirs>=2.5.0", "pyproject-hooks", diff --git a/src/hatch/template/default.py b/src/hatch/template/default.py index 71acb3e97..450784352 100644 --- a/src/hatch/template/default.py +++ b/src/hatch/template/default.py @@ -38,7 +38,7 @@ def initialize_config(self, config): license_file_name = f'{license_id}.txt' cached_license_path = cached_licenses_dir / license_file_name if not cached_license_path.is_file(): - from hatchling.licenses.supported import VERSION + from packaging.licenses._spdx import VERSION # noqa: PLC2701 url = f'https://raw.githubusercontent.com/spdx/license-list-data/v{VERSION}/text/{license_file_name}' for _ in range(5): diff --git a/tests/backend/licenses/__init__.py b/tests/backend/licenses/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/backend/licenses/test_parse.py b/tests/backend/licenses/test_parse.py deleted file mode 100644 index 9480a7c37..000000000 --- a/tests/backend/licenses/test_parse.py +++ /dev/null @@ -1,56 +0,0 @@ -import re - -import pytest - -from hatchling.licenses.parse import normalize_license_expression - - -@pytest.mark.parametrize( - 'expression', - [ - 'or', - 'and', - 'with', - 'mit or', - 'mit and', - 'mit with', - 'or mit', - 'and mit', - 'with mit', - '(mit', - 'mit)', - 'mit or or apache-2.0', - 'mit or apache-2.0 (bsd-3-clause and MPL-2.0)', - ], -) -def test_syntax_errors(expression): - with pytest.raises(ValueError, match=re.escape(f'invalid license expression: {expression}')): - normalize_license_expression(expression) - - -def test_unknown_license(): - with pytest.raises(ValueError, match='unknown license: foo'): - normalize_license_expression('mit or foo') - - -def test_unknown_license_exception(): - with pytest.raises(ValueError, match='unknown license exception: foo'): - normalize_license_expression('mit with foo') - - -@pytest.mark.parametrize( - ('raw', 'normalized'), - [ - ('mIt', 'MIT'), - ('mit or apache-2.0', 'MIT OR Apache-2.0'), - ('mit and apache-2.0', 'MIT AND Apache-2.0'), - ('gpl-2.0-or-later with bison-exception-2.2', 'GPL-2.0-or-later WITH Bison-exception-2.2'), - ('mit or apache-2.0 and (bsd-3-clause or mpl-2.0)', 'MIT OR Apache-2.0 AND (BSD-3-Clause OR MPL-2.0)'), - ('mit and (apache-2.0+ or mpl-2.0+)', 'MIT AND (Apache-2.0+ OR MPL-2.0+)'), - # Valid non-SPDX values - ('licenseref-public-domain', 'LicenseRef-Public-Domain'), - ('licenseref-proprietary', 'LicenseRef-Proprietary'), - ], -) -def test_normalization(raw, normalized): - assert normalize_license_expression(raw) == normalized diff --git a/tests/backend/licenses/test_supported.py b/tests/backend/licenses/test_supported.py deleted file mode 100644 index fbbb7adfe..000000000 --- a/tests/backend/licenses/test_supported.py +++ /dev/null @@ -1,31 +0,0 @@ -from hatchling.licenses.supported import EXCEPTIONS, LICENSES - - -def test_licenses(): - assert isinstance(LICENSES, dict) - assert list(LICENSES) == sorted(LICENSES) - - for name, data in LICENSES.items(): - assert isinstance(data, dict) - - assert 'id' in data - assert isinstance(data['id'], str) - assert data['id'].lower() == name - - assert 'deprecated' in data - assert isinstance(data['deprecated'], bool) - - -def test_exceptions(): - assert isinstance(EXCEPTIONS, dict) - assert list(EXCEPTIONS) == sorted(EXCEPTIONS) - - for name, data in EXCEPTIONS.items(): - assert isinstance(data, dict) - - assert 'id' in data - assert isinstance(data['id'], str) - assert data['id'].lower() == name - - assert 'deprecated' in data - assert isinstance(data['deprecated'], bool) diff --git a/tests/backend/metadata/test_core.py b/tests/backend/metadata/test_core.py index 76fd6601f..c401b8f3c 100644 --- a/tests/backend/metadata/test_core.py +++ b/tests/backend/metadata/test_core.py @@ -558,7 +558,7 @@ def test_normalization(self, isolation): def test_invalid_expression(self, isolation): metadata = ProjectMetadata(str(isolation), None, {'project': {'license': 'mit or foo'}}) - with pytest.raises(ValueError, match='Error parsing field `project.license` - unknown license: foo'): + with pytest.raises(ValueError, match="Error parsing field `project.license` - Unknown license: 'foo'"): _ = metadata.core.license_expression def test_multiple_options(self, isolation): From 28f233c535508247ffa9a40dab82c1d1cb2b700b Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sat, 9 Nov 2024 16:22:35 -0500 Subject: [PATCH 09/10] Update the default version of core metadata to 2.4 (#1790) --- backend/src/hatchling/metadata/core.py | 66 +--- backend/src/hatchling/metadata/spec.py | 93 ++++- docs/history/hatchling.md | 5 + tests/backend/builders/test_wheel.py | 2 +- tests/backend/metadata/test_core.py | 68 +--- tests/backend/metadata/test_spec.py | 451 ++++++++++++++++++++++++- 6 files changed, 558 insertions(+), 127 deletions(-) diff --git a/backend/src/hatchling/metadata/core.py b/backend/src/hatchling/metadata/core.py index 97359325d..32e6a2f70 100644 --- a/backend/src/hatchling/metadata/core.py +++ b/backend/src/hatchling/metadata/core.py @@ -734,65 +734,35 @@ def license_files(self) -> list[str]: https://peps.python.org/pep-0639/ """ if self._license_files is None: - if 'license-files' not in self.config: - data = {'globs': ['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']} - else: + if 'license-files' in self.config: + globs = self.config['license-files'] if 'license-files' in self.dynamic: message = ( 'Metadata field `license-files` cannot be both statically defined and ' 'listed in field `project.dynamic`' ) raise ValueError(message) + else: + globs = ['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*'] - data = self.config['license-files'] - if not isinstance(data, dict): - message = 'Field `project.license-files` must be a table' - raise TypeError(message) - - if 'paths' in data and 'globs' in data: - message = 'Cannot specify both `paths` and `globs` in the `project.license-files` table' - raise ValueError(message) - - license_files = [] - if 'paths' in data: - paths = data['paths'] - if not isinstance(paths, list): - message = 'Field `paths` in the `project.license-files` table must be an array' - raise TypeError(message) - - for i, relative_path in enumerate(paths, 1): - if not isinstance(relative_path, str): - message = f'Entry #{i} in field `paths` in the `project.license-files` table must be a string' - raise TypeError(message) + from glob import glob - path = os.path.normpath(os.path.join(self.root, relative_path)) - if not os.path.isfile(path): - message = f'License file does not exist: {relative_path}' - raise OSError(message) - - license_files.append(os.path.relpath(path, self.root).replace('\\', '/')) - elif 'globs' in data: - from glob import glob + license_files: list[str] = [] + if not isinstance(globs, list): + message = 'Field `project.license-files` must be an array' + raise TypeError(message) - globs = data['globs'] - if not isinstance(globs, list): - message = 'Field `globs` in the `project.license-files` table must be an array' + for i, pattern in enumerate(globs, 1): + if not isinstance(pattern, str): + message = f'Entry #{i} of field `project.license-files` must be a string' raise TypeError(message) - for i, pattern in enumerate(globs, 1): - if not isinstance(pattern, str): - message = f'Entry #{i} in field `globs` in the `project.license-files` table must be a string' - raise TypeError(message) - - full_pattern = os.path.normpath(os.path.join(self.root, pattern)) - license_files.extend( - os.path.relpath(path, self.root).replace('\\', '/') - for path in glob(full_pattern) - if os.path.isfile(path) - ) - else: - message = 'Must specify either `paths` or `globs` in the `project.license-files` table if defined' - raise ValueError(message) + full_pattern = os.path.normpath(os.path.join(self.root, pattern)) + license_files.extend( + os.path.relpath(path, self.root).replace('\\', '/') + for path in glob(full_pattern) + if os.path.isfile(path) + ) self._license_files = sorted(license_files) diff --git a/backend/src/hatchling/metadata/spec.py b/backend/src/hatchling/metadata/spec.py index a83ea3db1..3f9154101 100644 --- a/backend/src/hatchling/metadata/spec.py +++ b/backend/src/hatchling/metadata/spec.py @@ -5,8 +5,8 @@ if TYPE_CHECKING: from hatchling.metadata.core import ProjectMetadata -DEFAULT_METADATA_VERSION = '2.3' -LATEST_METADATA_VERSION = '2.3' +DEFAULT_METADATA_VERSION = '2.4' +LATEST_METADATA_VERSION = '2.4' CORE_METADATA_PROJECT_FIELDS = { 'Author': ('authors',), 'Author-email': ('authors',), @@ -56,6 +56,7 @@ def get_core_metadata_constructors() -> dict[str, Callable]: '2.1': construct_metadata_file_2_1, '2.2': construct_metadata_file_2_2, '2.3': construct_metadata_file_2_3, + '2.4': construct_metadata_file_2_4, } @@ -102,7 +103,7 @@ def project_metadata_from_core_metadata(core_metadata: str) -> dict[str, Any]: metadata['license'] = {'text': license_text} if (license_files := message.get_all('License-File')) is not None: - metadata['license-files'] = {'paths': license_files} + metadata['license-files'] = license_files if (summary := message.get('Summary')) is not None: metadata['description'] = summary @@ -430,12 +431,96 @@ def construct_metadata_file_2_2(metadata: ProjectMetadata, extra_dependencies: t def construct_metadata_file_2_3(metadata: ProjectMetadata, extra_dependencies: tuple[str] | None = None) -> str: """ - https://peps.python.org/pep-0639/ + https://peps.python.org/pep-0685/ """ metadata_file = 'Metadata-Version: 2.3\n' metadata_file += f'Name: {metadata.core.raw_name}\n' metadata_file += f'Version: {metadata.version}\n' + if metadata.core.dynamic: + # Ordered set + for field in { + core_metadata_field: None + for project_field in metadata.core.dynamic + for core_metadata_field in PROJECT_CORE_METADATA_FIELDS.get(project_field, ()) + }: + metadata_file += f'Dynamic: {field}\n' + + if metadata.core.description: + metadata_file += f'Summary: {metadata.core.description}\n' + + if metadata.core.urls: + for label, url in metadata.core.urls.items(): + metadata_file += f'Project-URL: {label}, {url}\n' + + authors_data = metadata.core.authors_data + if authors_data['name']: + metadata_file += f"Author: {', '.join(authors_data['name'])}\n" + if authors_data['email']: + metadata_file += f"Author-email: {', '.join(authors_data['email'])}\n" + + maintainers_data = metadata.core.maintainers_data + if maintainers_data['name']: + metadata_file += f"Maintainer: {', '.join(maintainers_data['name'])}\n" + if maintainers_data['email']: + metadata_file += f"Maintainer-email: {', '.join(maintainers_data['email'])}\n" + + if metadata.core.license: + license_start = 'License: ' + indent = ' ' * (len(license_start) - 1) + metadata_file += license_start + + for i, line in enumerate(metadata.core.license.splitlines()): + if i == 0: + metadata_file += f'{line}\n' + else: + metadata_file += f'{indent}{line}\n' + + if metadata.core.keywords: + metadata_file += f"Keywords: {','.join(metadata.core.keywords)}\n" + + if metadata.core.classifiers: + for classifier in metadata.core.classifiers: + metadata_file += f'Classifier: {classifier}\n' + + if metadata.core.requires_python: + metadata_file += f'Requires-Python: {metadata.core.requires_python}\n' + + if metadata.core.dependencies: + for dependency in metadata.core.dependencies: + metadata_file += f'Requires-Dist: {dependency}\n' + + if extra_dependencies: + for dependency in extra_dependencies: + metadata_file += f'Requires-Dist: {dependency}\n' + + if metadata.core.optional_dependencies: + for option, dependencies in metadata.core.optional_dependencies.items(): + metadata_file += f'Provides-Extra: {option}\n' + for dependency in dependencies: + if ';' in dependency: + dep_name, dep_env_marker = dependency.split(';', maxsplit=1) + metadata_file += f'Requires-Dist: {dep_name}; ({dep_env_marker.strip()}) and extra == {option!r}\n' + elif '@ ' in dependency: + metadata_file += f'Requires-Dist: {dependency} ; extra == {option!r}\n' + else: + metadata_file += f'Requires-Dist: {dependency}; extra == {option!r}\n' + + if metadata.core.readme: + metadata_file += f'Description-Content-Type: {metadata.core.readme_content_type}\n' + metadata_file += f'\n{metadata.core.readme}' + + return metadata_file + + +def construct_metadata_file_2_4(metadata: ProjectMetadata, extra_dependencies: tuple[str] | None = None) -> str: + """ + https://peps.python.org/pep-0639/ + """ + metadata_file = 'Metadata-Version: 2.4\n' + metadata_file += f'Name: {metadata.core.raw_name}\n' + metadata_file += f'Version: {metadata.version}\n' + if metadata.core.dynamic: # Ordered set for field in { diff --git a/docs/history/hatchling.md b/docs/history/hatchling.md index eff824151..8da9c1cea 100644 --- a/docs/history/hatchling.md +++ b/docs/history/hatchling.md @@ -10,8 +10,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ***Added:*** +- Update the default version of core metadata to 2.4 - Bump the minimum supported version of `packaging` to 24.2 +***Fixed:*** + +- No longer write package metadata for license expressions and files for versions of core metadata prior to 2.4 + ## [1.25.0](https://github.com/pypa/hatch/releases/tag/hatchling-v1.25.0) - 2024-06-22 ## {: #hatchling-v1.25.0 } ***Changed:*** diff --git a/tests/backend/builders/test_wheel.py b/tests/backend/builders/test_wheel.py index bdc4e99dd..dceb4f37c 100644 --- a/tests/backend/builders/test_wheel.py +++ b/tests/backend/builders/test_wheel.py @@ -909,7 +909,7 @@ def test_default_multiple_licenses(self, hatch, helpers, config_file, temp_dir): (project_path / 'LICENSES' / 'test').mkdir() config = { - 'project': {'name': project_name, 'dynamic': ['version'], 'license-files': {'globs': ['LICENSES/*']}}, + 'project': {'name': project_name, 'dynamic': ['version'], 'license-files': ['LICENSES/*']}, 'tool': { 'hatch': { 'version': {'path': 'my_app/__about__.py'}, diff --git a/tests/backend/metadata/test_core.py b/tests/backend/metadata/test_core.py index c401b8f3c..c1c19588d 100644 --- a/tests/backend/metadata/test_core.py +++ b/tests/backend/metadata/test_core.py @@ -621,54 +621,16 @@ def test_dynamic(self, isolation): ): _ = metadata.core.license_files - def test_not_table(self, isolation): + def test_not_array(self, isolation): metadata = ProjectMetadata(str(isolation), None, {'project': {'license-files': 9000}}) - with pytest.raises(TypeError, match='Field `project.license-files` must be a table'): - _ = metadata.core.license_files - - def test_multiple_options(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license-files': {'paths': [], 'globs': []}}}) - - with pytest.raises( - ValueError, match='Cannot specify both `paths` and `globs` in the `project.license-files` table' - ): - _ = metadata.core.license_files - - def test_no_option(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license-files': {}}}) - - with pytest.raises( - ValueError, match='Must specify either `paths` or `globs` in the `project.license-files` table if defined' - ): - _ = metadata.core.license_files - - def test_paths_not_array(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license-files': {'paths': 9000}}}) - - with pytest.raises(TypeError, match='Field `paths` in the `project.license-files` table must be an array'): + with pytest.raises(TypeError, match='Field `project.license-files` must be an array'): _ = metadata.core.license_files - def test_paths_entry_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license-files': {'paths': [9000]}}}) - - with pytest.raises( - TypeError, match='Entry #1 in field `paths` in the `project.license-files` table must be a string' - ): - _ = metadata.core.license_files - - def test_globs_not_array(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license-files': {'globs': 9000}}}) - - with pytest.raises(TypeError, match='Field `globs` in the `project.license-files` table must be an array'): - _ = metadata.core.license_files - - def test_globs_entry_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license-files': {'globs': [9000]}}}) + def test_entry_not_string(self, isolation): + metadata = ProjectMetadata(str(isolation), None, {'project': {'license-files': [9000]}}) - with pytest.raises( - TypeError, match='Entry #1 in field `globs` in the `project.license-files` table must be a string' - ): + with pytest.raises(TypeError, match='Entry #1 of field `project.license-files` must be a string'): _ = metadata.core.license_files def test_default_globs_no_licenses(self, isolation): @@ -693,7 +655,7 @@ def test_default_globs_with_licenses(self, temp_dir): assert metadata.core.license_files == sorted(expected) def test_globs_with_licenses(self, temp_dir): - metadata = ProjectMetadata(str(temp_dir), None, {'project': {'license-files': {'globs': ['LICENSES/*']}}}) + metadata = ProjectMetadata(str(temp_dir), None, {'project': {'license-files': ['LICENSES/*']}}) licenses_dir = temp_dir / 'LICENSES' licenses_dir.mkdir() @@ -709,7 +671,7 @@ def test_paths_with_licenses(self, temp_dir): metadata = ProjectMetadata( str(temp_dir), None, - {'project': {'license-files': {'paths': ['LICENSES/Apache-2.0.txt', 'LICENSES/MIT.txt', 'COPYING']}}}, + {'project': {'license-files': ['LICENSES/Apache-2.0.txt', 'LICENSES/MIT.txt', 'COPYING']}}, ) licenses_dir = temp_dir / 'LICENSES' @@ -722,20 +684,6 @@ def test_paths_with_licenses(self, temp_dir): assert metadata.core.license_files == ['COPYING', 'LICENSES/Apache-2.0.txt', 'LICENSES/MIT.txt'] - def test_paths_missing_license(self, temp_dir): - metadata = ProjectMetadata( - str(temp_dir), - None, - {'project': {'license-files': {'paths': ['LICENSES/MIT.txt']}}}, - ) - - licenses_dir = temp_dir / 'LICENSES' - licenses_dir.mkdir() - (licenses_dir / 'Apache-2.0.txt').touch() - - with pytest.raises(OSError, match='License file does not exist: LICENSES/MIT.txt'): - _ = metadata.core.license_files - class TestAuthors: def test_dynamic(self, isolation): @@ -1661,7 +1609,7 @@ def test_license_files(self, temp_dir, latest_spec): raw_metadata = { 'name': 'My.App', 'version': '0.0.1', - 'license-files': {'paths': ['LICENSES/Apache-2.0.txt', 'LICENSES/MIT.txt']}, + 'license-files': ['LICENSES/Apache-2.0.txt', 'LICENSES/MIT.txt'], } metadata = ProjectMetadata(str(temp_dir), None, {'project': raw_metadata}) diff --git a/tests/backend/metadata/test_spec.py b/tests/backend/metadata/test_spec.py index 50a9b6073..569b710de 100644 --- a/tests/backend/metadata/test_spec.py +++ b/tests/backend/metadata/test_spec.py @@ -131,7 +131,7 @@ def test_license_files(self): assert project_metadata_from_core_metadata(core_metadata) == { 'name': 'My.App', 'version': '0.1.0', - 'license-files': {'paths': ['LICENSES/Apache-2.0.txt', 'LICENSES/MIT.txt']}, + 'license-files': ['LICENSES/Apache-2.0.txt', 'LICENSES/MIT.txt'], } def test_license_expression(self): @@ -1660,6 +1660,429 @@ def test_license(self, constructor, isolation, helpers): """ ) + def test_keywords_single(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'keywords': ['foo']}} + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.3 + Name: My.App + Version: 0.1.0 + Keywords: foo + """ + ) + + def test_keywords_multiple(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'keywords': ['foo', 'bar']}} + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.3 + Name: My.App + Version: 0.1.0 + Keywords: bar,foo + """ + ) + + def test_classifiers(self, constructor, isolation, helpers): + classifiers = [ + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.9', + ] + metadata = ProjectMetadata( + str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'classifiers': classifiers}} + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.3 + Name: My.App + Version: 0.1.0 + Classifier: Programming Language :: Python :: 3.9 + Classifier: Programming Language :: Python :: 3.11 + """ + ) + + def test_requires_python(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'requires-python': '>=1,<2'}} + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.3 + Name: My.App + Version: 0.1.0 + Requires-Python: <2,>=1 + """ + ) + + def test_dependencies(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), + None, + {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}}, + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.3 + Name: My.App + Version: 0.1.0 + Requires-Dist: bar==5 + Requires-Dist: foo==1 + """ + ) + + def test_optional_dependencies(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), + None, + { + 'project': { + 'name': 'My.App', + 'version': '0.1.0', + 'optional-dependencies': { + 'feature2': ['foo==1; python_version < "3"', 'bar==5'], + 'feature1': ['foo==1', 'bar==5; python_version < "3"'], + }, + } + }, + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.3 + Name: My.App + Version: 0.1.0 + Provides-Extra: feature1 + Requires-Dist: bar==5; (python_version < '3') and extra == 'feature1' + Requires-Dist: foo==1; extra == 'feature1' + Provides-Extra: feature2 + Requires-Dist: bar==5; extra == 'feature2' + Requires-Dist: foo==1; (python_version < '3') and extra == 'feature2' + """ + ) + + def test_extra_runtime_dependencies(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), + None, + {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}}, + ) + + assert constructor(metadata, extra_dependencies=['baz==9']) == helpers.dedent( + """ + Metadata-Version: 2.3 + Name: My.App + Version: 0.1.0 + Requires-Dist: bar==5 + Requires-Dist: foo==1 + Requires-Dist: baz==9 + """ + ) + + def test_readme(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), + None, + { + 'project': { + 'name': 'My.App', + 'version': '0.1.0', + 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, + } + }, + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.3 + Name: My.App + Version: 0.1.0 + Description-Content-Type: text/markdown + + test content + """ + ) + + def test_all(self, constructor, temp_dir, helpers): + metadata = ProjectMetadata( + str(temp_dir), + None, + { + 'project': { + 'name': 'My.App', + 'version': '0.1.0', + 'description': 'foo', + 'urls': {'foo': 'bar', 'bar': 'baz'}, + 'authors': [{'email': 'bar@domain', 'name': 'foo'}], + 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], + 'keywords': ['foo', 'bar'], + 'classifiers': [ + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.9', + ], + 'requires-python': '>=1,<2', + 'dependencies': ['foo==1', 'bar==5'], + 'optional-dependencies': { + 'feature2': ['foo==1; python_version < "3"', 'bar==5'], + 'feature1': ['foo==1', 'bar==5; python_version < "3"'], + 'feature3': ['baz @ file:///path/to/project'], + }, + 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, + }, + 'tool': {'hatch': {'metadata': {'allow-direct-references': True}}}, + }, + ) + + licenses_dir = temp_dir / 'LICENSES' + licenses_dir.mkdir() + (licenses_dir / 'MIT.txt').touch() + (licenses_dir / 'Apache-2.0.txt').touch() + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.3 + Name: My.App + Version: 0.1.0 + Summary: foo + Project-URL: foo, bar + Project-URL: bar, baz + Author-email: foo + Maintainer-email: foo + Keywords: bar,foo + Classifier: Programming Language :: Python :: 3.9 + Classifier: Programming Language :: Python :: 3.11 + Requires-Python: <2,>=1 + Requires-Dist: bar==5 + Requires-Dist: foo==1 + Provides-Extra: feature1 + Requires-Dist: bar==5; (python_version < '3') and extra == 'feature1' + Requires-Dist: foo==1; extra == 'feature1' + Provides-Extra: feature2 + Requires-Dist: bar==5; extra == 'feature2' + Requires-Dist: foo==1; (python_version < '3') and extra == 'feature2' + Provides-Extra: feature3 + Requires-Dist: baz@ file:///path/to/project ; extra == 'feature3' + Description-Content-Type: text/markdown + + test content + """ + ) + + +@pytest.mark.parametrize('constructor', [get_core_metadata_constructors()['2.4']]) +class TestCoreMetadataV24: + def test_default(self, constructor, isolation, helpers): + metadata = ProjectMetadata(str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0'}}) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.4 + Name: My.App + Version: 0.1.0 + """ + ) + + def test_description(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'description': 'foo'}} + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.4 + Name: My.App + Version: 0.1.0 + Summary: foo + """ + ) + + def test_dynamic(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), + None, + {'project': {'name': 'My.App', 'version': '0.1.0', 'dynamic': ['authors', 'classifiers']}}, + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.4 + Name: My.App + Version: 0.1.0 + Dynamic: Author + Dynamic: Author-email + Dynamic: Classifier + """ + ) + + def test_urls(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), + None, + {'project': {'name': 'My.App', 'version': '0.1.0', 'urls': {'foo': 'bar', 'bar': 'baz'}}}, + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.4 + Name: My.App + Version: 0.1.0 + Project-URL: foo, bar + Project-URL: bar, baz + """ + ) + + def test_authors_name(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}]}} + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.4 + Name: My.App + Version: 0.1.0 + Author: foo + """ + ) + + def test_authors_email(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), + None, + {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'foo@domain'}]}}, + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.4 + Name: My.App + Version: 0.1.0 + Author-email: foo@domain + """ + ) + + def test_authors_name_and_email(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), + None, + {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'bar@domain', 'name': 'foo'}]}}, + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.4 + Name: My.App + Version: 0.1.0 + Author-email: foo + """ + ) + + def test_authors_multiple(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), + None, + {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}, {'name': 'bar'}]}}, + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.4 + Name: My.App + Version: 0.1.0 + Author: foo, bar + """ + ) + + def test_maintainers_name(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}]}} + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.4 + Name: My.App + Version: 0.1.0 + Maintainer: foo + """ + ) + + def test_maintainers_email(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), + None, + {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'email': 'foo@domain'}]}}, + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.4 + Name: My.App + Version: 0.1.0 + Maintainer-email: foo@domain + """ + ) + + def test_maintainers_name_and_email(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), + None, + { + 'project': { + 'name': 'My.App', + 'version': '0.1.0', + 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], + } + }, + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.4 + Name: My.App + Version: 0.1.0 + Maintainer-email: foo + """ + ) + + def test_maintainers_multiple(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), + None, + {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}, {'name': 'bar'}]}}, + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.4 + Name: My.App + Version: 0.1.0 + Maintainer: foo, bar + """ + ) + + def test_license(self, constructor, isolation, helpers): + metadata = ProjectMetadata( + str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'license': {'text': 'foo\nbar'}}} + ) + + assert constructor(metadata) == helpers.dedent( + """ + Metadata-Version: 2.4 + Name: My.App + Version: 0.1.0 + License: foo + bar + """ + ) + def test_license_expression(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), @@ -1669,7 +2092,7 @@ def test_license_expression(self, constructor, isolation, helpers): assert constructor(metadata) == helpers.dedent( """ - Metadata-Version: 2.3 + Metadata-Version: 2.4 Name: My.App Version: 0.1.0 License-Expression: MIT OR Apache-2.0 @@ -1680,7 +2103,7 @@ def test_license_files(self, constructor, temp_dir, helpers): metadata = ProjectMetadata( str(temp_dir), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'license-files': {'globs': ['LICENSES/*']}}}, + {'project': {'name': 'My.App', 'version': '0.1.0', 'license-files': ['LICENSES/*']}}, ) licenses_dir = temp_dir / 'LICENSES' @@ -1690,7 +2113,7 @@ def test_license_files(self, constructor, temp_dir, helpers): assert constructor(metadata) == helpers.dedent( """ - Metadata-Version: 2.3 + Metadata-Version: 2.4 Name: My.App Version: 0.1.0 License-File: LICENSES/Apache-2.0.txt @@ -1705,7 +2128,7 @@ def test_keywords_single(self, constructor, isolation, helpers): assert constructor(metadata) == helpers.dedent( """ - Metadata-Version: 2.3 + Metadata-Version: 2.4 Name: My.App Version: 0.1.0 Keywords: foo @@ -1719,7 +2142,7 @@ def test_keywords_multiple(self, constructor, isolation, helpers): assert constructor(metadata) == helpers.dedent( """ - Metadata-Version: 2.3 + Metadata-Version: 2.4 Name: My.App Version: 0.1.0 Keywords: bar,foo @@ -1737,7 +2160,7 @@ def test_classifiers(self, constructor, isolation, helpers): assert constructor(metadata) == helpers.dedent( """ - Metadata-Version: 2.3 + Metadata-Version: 2.4 Name: My.App Version: 0.1.0 Classifier: Programming Language :: Python :: 3.9 @@ -1752,7 +2175,7 @@ def test_requires_python(self, constructor, isolation, helpers): assert constructor(metadata) == helpers.dedent( """ - Metadata-Version: 2.3 + Metadata-Version: 2.4 Name: My.App Version: 0.1.0 Requires-Python: <2,>=1 @@ -1768,7 +2191,7 @@ def test_dependencies(self, constructor, isolation, helpers): assert constructor(metadata) == helpers.dedent( """ - Metadata-Version: 2.3 + Metadata-Version: 2.4 Name: My.App Version: 0.1.0 Requires-Dist: bar==5 @@ -1794,7 +2217,7 @@ def test_optional_dependencies(self, constructor, isolation, helpers): assert constructor(metadata) == helpers.dedent( """ - Metadata-Version: 2.3 + Metadata-Version: 2.4 Name: My.App Version: 0.1.0 Provides-Extra: feature1 @@ -1815,7 +2238,7 @@ def test_extra_runtime_dependencies(self, constructor, isolation, helpers): assert constructor(metadata, extra_dependencies=['baz==9']) == helpers.dedent( """ - Metadata-Version: 2.3 + Metadata-Version: 2.4 Name: My.App Version: 0.1.0 Requires-Dist: bar==5 @@ -1839,7 +2262,7 @@ def test_readme(self, constructor, isolation, helpers): assert constructor(metadata) == helpers.dedent( """ - Metadata-Version: 2.3 + Metadata-Version: 2.4 Name: My.App Version: 0.1.0 Description-Content-Type: text/markdown @@ -1861,7 +2284,7 @@ def test_all(self, constructor, temp_dir, helpers): 'authors': [{'email': 'bar@domain', 'name': 'foo'}], 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], 'license': 'mit or apache-2.0', - 'license-files': {'globs': ['LICENSES/*']}, + 'license-files': ['LICENSES/*'], 'keywords': ['foo', 'bar'], 'classifiers': [ 'Programming Language :: Python :: 3.11', @@ -1887,7 +2310,7 @@ def test_all(self, constructor, temp_dir, helpers): assert constructor(metadata) == helpers.dedent( """ - Metadata-Version: 2.3 + Metadata-Version: 2.4 Name: My.App Version: 0.1.0 Summary: foo From a664f3c1c56afbec581e9d44e7db1f3beaa87b0c Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Sat, 9 Nov 2024 21:41:25 +0000 Subject: [PATCH 10/10] Add pixi to excluded directories (#1762) --- backend/src/hatchling/builders/constants.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/hatchling/builders/constants.py b/backend/src/hatchling/builders/constants.py index b16b199bf..ac718da73 100644 --- a/backend/src/hatchling/builders/constants.py +++ b/backend/src/hatchling/builders/constants.py @@ -21,6 +21,8 @@ '.pytest_cache', # Mypy '.mypy_cache', + # pixi + '.pixi', )) EXCLUDED_FILES = frozenset(( # https://en.wikipedia.org/wiki/.DS_Store