From 7c8dbbcf66299efe4039b9d7b1d02b17b707aab7 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 1 Aug 2024 11:49:18 -0400 Subject: [PATCH 01/16] fix typo (#1653) --- docs/tutorials/environment/basic-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/environment/basic-usage.md b/docs/tutorials/environment/basic-usage.md index 99e1c491b..8372ce407 100644 --- a/docs/tutorials/environment/basic-usage.md +++ b/docs/tutorials/environment/basic-usage.md @@ -135,7 +135,7 @@ Once the environment is active, you can run commands like you would in any Pytho Notice below that when running `pip list` in the test environment, you can see: -1. That you package is installed in editable mode. +1. That your package is installed in editable mode. 2. That the environment contains both `pytest` and `pytest-cov` as specified above in the `pyproject.toml` file. ``` From 551c26e087be6399dc3614106ad08bd61a05b32f Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 17 Aug 2024 16:59:02 +0200 Subject: [PATCH 02/16] Assorted MyPy improvments (#1663) --- backend/src/hatchling/builders/custom.py | 4 ++-- backend/src/hatchling/builders/hooks/custom.py | 2 +- backend/src/hatchling/builders/hooks/plugin/hooks.py | 2 +- backend/src/hatchling/builders/plugin/hooks.py | 2 +- backend/src/hatchling/cli/dep/core.py | 4 ++-- backend/src/hatchling/metadata/custom.py | 4 ++-- backend/src/hatchling/metadata/plugin/hooks.py | 2 +- backend/src/hatchling/metadata/spec.py | 2 +- backend/src/hatchling/plugin/utils.py | 4 ++-- backend/src/hatchling/version/core.py | 2 +- backend/src/hatchling/version/source/code.py | 4 ++-- pyproject.toml | 2 +- src/hatch/cli/terminal.py | 8 ++++---- src/hatch/dep/sync.py | 4 ++-- src/hatch/env/collectors/custom.py | 2 +- src/hatch/env/collectors/plugin/hooks.py | 2 +- src/hatch/plugin/utils.py | 4 ++-- src/hatch/utils/platform.py | 2 +- 18 files changed, 28 insertions(+), 28 deletions(-) diff --git a/backend/src/hatchling/builders/custom.py b/backend/src/hatchling/builders/custom.py index 29c80317e..88fa5b004 100644 --- a/backend/src/hatchling/builders/custom.py +++ b/backend/src/hatchling/builders/custom.py @@ -16,7 +16,7 @@ class CustomBuilder(Generic[PluginManagerBound]): PLUGIN_NAME = 'custom' - def __new__( # type: ignore + def __new__( # type: ignore[misc] cls, root: str, plugin_manager: PluginManagerBound | None = None, @@ -45,7 +45,7 @@ def __new__( # type: ignore message = f'Build script does not exist: {build_script}' raise OSError(message) - hook_class = load_plugin_from_script(path, build_script, BuilderInterface, 'builder') # type: ignore + hook_class = load_plugin_from_script(path, build_script, BuilderInterface, 'builder') # type: ignore[type-abstract] hook = hook_class(root, plugin_manager=plugin_manager, config=config, metadata=metadata, app=app) # Always keep the name to avoid confusion diff --git a/backend/src/hatchling/builders/hooks/custom.py b/backend/src/hatchling/builders/hooks/custom.py index 1af099308..a1f75a886 100644 --- a/backend/src/hatchling/builders/hooks/custom.py +++ b/backend/src/hatchling/builders/hooks/custom.py @@ -11,7 +11,7 @@ class CustomBuildHook: PLUGIN_NAME = 'custom' - def __new__( # type: ignore + def __new__( # type: ignore[misc] cls, root: str, config: dict[str, Any], diff --git a/backend/src/hatchling/builders/hooks/plugin/hooks.py b/backend/src/hatchling/builders/hooks/plugin/hooks.py index 53fb28897..417521eda 100644 --- a/backend/src/hatchling/builders/hooks/plugin/hooks.py +++ b/backend/src/hatchling/builders/hooks/plugin/hooks.py @@ -12,4 +12,4 @@ @hookimpl def hatch_register_build_hook() -> list[type[BuildHookInterface]]: - return [CustomBuildHook, VersionBuildHook] # type: ignore + return [CustomBuildHook, VersionBuildHook] # type: ignore[list-item] diff --git a/backend/src/hatchling/builders/plugin/hooks.py b/backend/src/hatchling/builders/plugin/hooks.py index 47da933be..97a0e08b6 100644 --- a/backend/src/hatchling/builders/plugin/hooks.py +++ b/backend/src/hatchling/builders/plugin/hooks.py @@ -15,4 +15,4 @@ @hookimpl def hatch_register_builder() -> list[type[BuilderInterface]]: - return [AppBuilder, BinaryBuilder, CustomBuilder, SdistBuilder, WheelBuilder] # type: ignore + return [AppBuilder, BinaryBuilder, CustomBuilder, SdistBuilder, WheelBuilder] # type: ignore[list-item] diff --git a/backend/src/hatchling/cli/dep/core.py b/backend/src/hatchling/cli/dep/core.py index 73c643e42..65101603d 100644 --- a/backend/src/hatchling/cli/dep/core.py +++ b/backend/src/hatchling/cli/dep/core.py @@ -126,7 +126,7 @@ def dependencies_in_sync( if sys_path is None: sys_path = sys.path if environment is None: - environment = default_environment() # type: ignore + environment = default_environment() # type: ignore[assignment] installed_distributions = DistributionCache(sys_path) - return all(dependency_in_sync(requirement, environment, installed_distributions) for requirement in requirements) # type: ignore + return all(dependency_in_sync(requirement, environment, installed_distributions) for requirement in requirements) # type: ignore[arg-type] diff --git a/backend/src/hatchling/metadata/custom.py b/backend/src/hatchling/metadata/custom.py index 148d14b95..2bf563f3b 100644 --- a/backend/src/hatchling/metadata/custom.py +++ b/backend/src/hatchling/metadata/custom.py @@ -11,7 +11,7 @@ class CustomMetadataHook: PLUGIN_NAME = 'custom' - def __new__( # type: ignore + def __new__( # type: ignore[misc] cls, root: str, config: dict[str, Any], @@ -32,7 +32,7 @@ def __new__( # type: ignore message = f'Build script does not exist: {build_script}' raise OSError(message) - hook_class = load_plugin_from_script(path, build_script, MetadataHookInterface, 'metadata_hook') # type: ignore + hook_class = load_plugin_from_script(path, build_script, MetadataHookInterface, 'metadata_hook') # type: ignore[type-abstract] hook = hook_class(root, config, *args, **kwargs) # Always keep the name to avoid confusion diff --git a/backend/src/hatchling/metadata/plugin/hooks.py b/backend/src/hatchling/metadata/plugin/hooks.py index 7e8e36774..f6d710358 100644 --- a/backend/src/hatchling/metadata/plugin/hooks.py +++ b/backend/src/hatchling/metadata/plugin/hooks.py @@ -11,4 +11,4 @@ @hookimpl def hatch_register_metadata_hook() -> type[MetadataHookInterface]: - return CustomMetadataHook # type: ignore + return CustomMetadataHook # type: ignore[return-value] diff --git a/backend/src/hatchling/metadata/spec.py b/backend/src/hatchling/metadata/spec.py index 501a88260..16cb6d1c5 100644 --- a/backend/src/hatchling/metadata/spec.py +++ b/backend/src/hatchling/metadata/spec.py @@ -67,7 +67,7 @@ def project_metadata_from_core_metadata(core_metadata: str) -> dict[str, Any]: header_registry = HeaderRegistry() message = email.message_from_string(core_metadata) - metadata = {} + metadata: dict[str, Any] = {} if name := message.get('Name'): metadata['name'] = name diff --git a/backend/src/hatchling/plugin/utils.py b/backend/src/hatchling/plugin/utils.py index 7d37e4820..841205422 100644 --- a/backend/src/hatchling/plugin/utils.py +++ b/backend/src/hatchling/plugin/utils.py @@ -14,8 +14,8 @@ def load_plugin_from_script(path: str, script_name: str, plugin_class: type[T], from importlib.util import module_from_spec, spec_from_file_location spec = spec_from_file_location(script_name, path) - module = module_from_spec(spec) # type: ignore - spec.loader.exec_module(module) # type: ignore + module = module_from_spec(spec) # type: ignore[arg-type] + spec.loader.exec_module(module) # type: ignore[union-attr] plugin_finder = f'get_{plugin_id}' names = dir(module) diff --git a/backend/src/hatchling/version/core.py b/backend/src/hatchling/version/core.py index 9b1756f0f..b56fd3733 100644 --- a/backend/src/hatchling/version/core.py +++ b/backend/src/hatchling/version/core.py @@ -43,7 +43,7 @@ def read(self, *, pattern: str | bool) -> str: return self.__cached_read_data[0] def set_version(self, version: str) -> None: - _old_version, file_contents, (start, end) = self.__cached_read_data # type: ignore + _old_version, file_contents, (start, end) = self.__cached_read_data # type: ignore[misc] with open(self.__path, 'w', encoding='utf-8') as f: f.write(f'{file_contents[:start]}{version}{file_contents[end:]}') diff --git a/backend/src/hatchling/version/source/code.py b/backend/src/hatchling/version/source/code.py index 9433a0c2d..dc708aee7 100644 --- a/backend/src/hatchling/version/source/code.py +++ b/backend/src/hatchling/version/source/code.py @@ -45,12 +45,12 @@ def get_version_data(self) -> dict: absolute_search_paths.append(os.path.normpath(os.path.join(self.root, search_path))) spec = spec_from_file_location(os.path.splitext(path)[0], path) - module = module_from_spec(spec) # type: ignore + module = module_from_spec(spec) # type: ignore[arg-type] old_search_paths = list(sys.path) try: sys.path[:] = [*absolute_search_paths, *old_search_paths] - spec.loader.exec_module(module) # type: ignore + spec.loader.exec_module(module) # type: ignore[union-attr] finally: sys.path[:] = old_search_paths diff --git a/pyproject.toml b/pyproject.toml index b142e00cc..5d8b689e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,11 +90,11 @@ exclude = [ [tool.mypy] disallow_untyped_defs = false disallow_incomplete_defs = false +enable_error_code = ["ignore-without-code", "truthy-bool"] follow_imports = "normal" ignore_missing_imports = true pretty = true show_column_numbers = true -show_error_codes = true warn_no_return = false warn_unused_ignores = true diff --git a/src/hatch/cli/terminal.py b/src/hatch/cli/terminal.py index 9f3cb7cb9..ca79bf755 100644 --- a/src/hatch/cli/terminal.py +++ b/src/hatch/cli/terminal.py @@ -42,8 +42,8 @@ def __init__( is_interactive: bool, verbosity: int, spinner_style: str, - waiting_style: Style, - success_style: Style, + waiting_style: Style | str, + success_style: Style | str, initializer: Callable, finalizer: Callable, ): @@ -331,8 +331,8 @@ def status(self) -> BorrowedStatus: is_interactive=self.console.is_interactive, verbosity=self.verbosity, spinner_style=self._style_spinner, - waiting_style=self._style_level_waiting, # type: ignore[arg-type] - success_style=self._style_level_success, # type: ignore[arg-type] + waiting_style=self._style_level_waiting, + success_style=self._style_level_success, initializer=lambda: setattr(self.platform, 'displaying_status', True), # type: ignore[attr-defined] finalizer=lambda: setattr(self.platform, 'displaying_status', False), # type: ignore[attr-defined] ) diff --git a/src/hatch/dep/sync.py b/src/hatch/dep/sync.py index 73c643e42..65101603d 100644 --- a/src/hatch/dep/sync.py +++ b/src/hatch/dep/sync.py @@ -126,7 +126,7 @@ def dependencies_in_sync( if sys_path is None: sys_path = sys.path if environment is None: - environment = default_environment() # type: ignore + environment = default_environment() # type: ignore[assignment] installed_distributions = DistributionCache(sys_path) - return all(dependency_in_sync(requirement, environment, installed_distributions) for requirement in requirements) # type: ignore + return all(dependency_in_sync(requirement, environment, installed_distributions) for requirement in requirements) # type: ignore[arg-type] diff --git a/src/hatch/env/collectors/custom.py b/src/hatch/env/collectors/custom.py index 287c3a938..bfbc11a6f 100644 --- a/src/hatch/env/collectors/custom.py +++ b/src/hatch/env/collectors/custom.py @@ -11,7 +11,7 @@ class CustomEnvironmentCollector: PLUGIN_NAME = 'custom' - def __new__( # type: ignore + def __new__( # type: ignore[misc] cls, root: str, config: dict[str, Any], diff --git a/src/hatch/env/collectors/plugin/hooks.py b/src/hatch/env/collectors/plugin/hooks.py index 7ba66683b..102368835 100644 --- a/src/hatch/env/collectors/plugin/hooks.py +++ b/src/hatch/env/collectors/plugin/hooks.py @@ -12,4 +12,4 @@ @hookimpl def hatch_register_environment_collector() -> list[type[EnvironmentCollectorInterface]]: - return [CustomEnvironmentCollector, DefaultEnvironmentCollector] # type: ignore + return [CustomEnvironmentCollector, DefaultEnvironmentCollector] # type: ignore[list-item] diff --git a/src/hatch/plugin/utils.py b/src/hatch/plugin/utils.py index b2e83da6e..9991ef7ce 100644 --- a/src/hatch/plugin/utils.py +++ b/src/hatch/plugin/utils.py @@ -12,8 +12,8 @@ def load_plugin_from_script( from importlib.util import module_from_spec, spec_from_file_location spec = spec_from_file_location(script_name, path) - module = module_from_spec(spec) # type: ignore - spec.loader.exec_module(module) # type: ignore + module = module_from_spec(spec) # type: ignore[arg-type] + spec.loader.exec_module(module) # type: ignore[union-attr] plugin_finder = f'get_{plugin_id}' names = dir(module) diff --git a/src/hatch/utils/platform.py b/src/hatch/utils/platform.py index 04eca142c..6e7b45183 100644 --- a/src/hatch/utils/platform.py +++ b/src/hatch/utils/platform.py @@ -176,7 +176,7 @@ def populate_default_popen_kwargs(self, kwargs: dict[str, Any], *, shell: bool) @staticmethod def stream_process_output(process: Popen) -> Iterable[str]: # To avoid blocking never use a pipe's file descriptor iterator. See https://bugs.python.org/issue3907 - for line in iter(process.stdout.readline, b''): # type: ignore + for line in iter(process.stdout.readline, b''): # type: ignore[union-attr] yield line.decode('utf-8') @property From 70b974c378f642689fb089b8bb2bfe6cbb831203 Mon Sep 17 00:00:00 2001 From: Orkun Konak <73122679+Orkunnnn@users.noreply.github.com> Date: Sat, 17 Aug 2024 22:31:17 +0300 Subject: [PATCH 03/16] docs: fix outdated `hatch run` commands (#1658) Co-authored-by: Ofek Lev --- docs/community/contributing.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/community/contributing.md b/docs/community/contributing.md index 67b299de3..4a7e9763b 100644 --- a/docs/community/contributing.md +++ b/docs/community/contributing.md @@ -23,13 +23,13 @@ git checkout -b add-my-contribution Run the test suite while developing: ```bash -hatch run dev +hatch test ``` Run the test suite with coverage report: ```bash -hatch run cov +hatch test --cover ``` Run the extended test suite with coverage: @@ -43,13 +43,13 @@ hatch run full Run automated formatting: ```bash -hatch run lint:fmt +hatch fmt --formatter ``` Run full linting and type checking: ```bash -hatch run lint:all +hatch fmt ``` ## Docs From e07d2984aba528262616b70891860e5aefb12775 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Sat, 17 Aug 2024 12:49:19 -0700 Subject: [PATCH 04/16] Support `hatch version --force` (#1645) Co-authored-by: Ofek Lev --- backend/src/hatchling/utils/constants.py | 4 ++ .../version/scheme/plugin/interface.py | 30 ++++++++++++- .../src/hatchling/version/scheme/standard.py | 2 +- docs/community/contributing.md | 13 +++--- docs/history/hatch.md | 1 + docs/plugins/version-scheme/reference.md | 1 + src/hatch/cli/version/__init__.py | 30 +++++-------- src/hatch/config/constants.py | 4 ++ tests/backend/version/scheme/test_standard.py | 11 ++++- tests/cli/version/test_version.py | 45 +++++++++++++++++++ 10 files changed, 112 insertions(+), 29 deletions(-) diff --git a/backend/src/hatchling/utils/constants.py b/backend/src/hatchling/utils/constants.py index 5c3c4e291..e3349c7b5 100644 --- a/backend/src/hatchling/utils/constants.py +++ b/backend/src/hatchling/utils/constants.py @@ -1,2 +1,6 @@ DEFAULT_BUILD_SCRIPT = 'hatch_build.py' DEFAULT_CONFIG_FILE = 'hatch.toml' + + +class VersionEnvVars: + VALIDATE_BUMP = 'HATCH_VERSION_VALIDATE_BUMP' diff --git a/backend/src/hatchling/version/scheme/plugin/interface.py b/backend/src/hatchling/version/scheme/plugin/interface.py index 0e38b150a..6cf12aa5e 100644 --- a/backend/src/hatchling/version/scheme/plugin/interface.py +++ b/backend/src/hatchling/version/scheme/plugin/interface.py @@ -1,6 +1,8 @@ from __future__ import annotations +import os from abc import ABC, abstractmethod +from functools import cached_property class VersionSchemeInterface(ABC): # no cov @@ -51,9 +53,33 @@ def config(self) -> dict: """ return self.__config + @cached_property + def validate_bump(self) -> bool: + """ + This is the value of the `validate-bump` option, with the `HATCH_VERSION_VALIDATE_BUMP` + environment variable taking precedence. Validation is enabled by default. + + ```toml config-example + [tool.hatch.version] + validate-bump = true + ``` + """ + from hatchling.utils.constants import VersionEnvVars + + if VersionEnvVars.VALIDATE_BUMP in os.environ: + return os.environ[VersionEnvVars.VALIDATE_BUMP] not in {'false', '0'} + + validate_bump = self.config.get('validate-bump', True) + if not isinstance(validate_bump, bool): + message = 'option `validate-bump` must be a boolean' + raise TypeError(message) + + return validate_bump + @abstractmethod def update(self, desired_version: str, original_version: str, version_data: dict) -> str: """ - This should return a normalized form of the desired version and verify that it - is higher than the original version. + This should return a normalized form of the desired version. If the + [validate_bump](reference.md#hatchling.version.scheme.plugin.interface.VersionSchemeInterface.validate_bump) + property is `True`, this method should also verify that the version is higher than the original version. """ diff --git a/backend/src/hatchling/version/scheme/standard.py b/backend/src/hatchling/version/scheme/standard.py index 4ec00893a..5c3caebba 100644 --- a/backend/src/hatchling/version/scheme/standard.py +++ b/backend/src/hatchling/version/scheme/standard.py @@ -57,7 +57,7 @@ def update( raise ValueError(message) next_version = Version(version) - if self.config.get('validate-bump', True) and next_version <= original: + if self.validate_bump and next_version <= original: message = f'Version `{version}` is not higher than the original version `{original_version}`' raise ValueError(message) diff --git a/docs/community/contributing.md b/docs/community/contributing.md index 4a7e9763b..88ad71b5d 100644 --- a/docs/community/contributing.md +++ b/docs/community/contributing.md @@ -5,7 +5,7 @@ The usual process to make a contribution is to: 1. Check for existing related issues 2. Fork the repository and create a new branch 3. Make your changes -4. Make sure formatting, linting and tests passes. +4. Make sure formatting, linting and tests passes. 5. Add tests if possible to cover the lines you added. 6. Commit, and send a Pull Request. @@ -15,7 +15,7 @@ Clone the `hatch` repository, `cd` into it, and create a new branch for your con ```bash cd hatch -git checkout -b add-my-contribution +git switch -c add-my-contribution ``` ## Run the tests @@ -35,7 +35,7 @@ hatch test --cover Run the extended test suite with coverage: ```bash -hatch run full +hatch test --cover --all ``` ## Lint @@ -43,13 +43,14 @@ hatch run full Run automated formatting: ```bash -hatch fmt --formatter +hatch fmt ``` Run full linting and type checking: ```bash -hatch fmt +hatch fmt --check +hatch run types:check ``` ## Docs @@ -64,4 +65,4 @@ Build and validate the documentation website: ```bash hatch run docs:build-check -``` \ No newline at end of file +``` diff --git a/docs/history/hatch.md b/docs/history/hatch.md index 0eac19044..cedeee268 100644 --- a/docs/history/hatch.md +++ b/docs/history/hatch.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ***Added:*** - The `version` and `project metadata` commands now support projects that do not use Hatchling as the build backend +- 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` diff --git a/docs/plugins/version-scheme/reference.md b/docs/plugins/version-scheme/reference.md index 4affdc497..f57404ddd 100644 --- a/docs/plugins/version-scheme/reference.md +++ b/docs/plugins/version-scheme/reference.md @@ -12,4 +12,5 @@ - PLUGIN_NAME - root - config + - validate_bump - update diff --git a/src/hatch/cli/version/__init__.py b/src/hatch/cli/version/__init__.py index 143ce60cc..b89bfc3ec 100644 --- a/src/hatch/cli/version/__init__.py +++ b/src/hatch/cli/version/__init__.py @@ -10,8 +10,14 @@ @click.command(short_help="View or set a project's version") @click.argument('desired_version', required=False) +@click.option( + '--force', + '-f', + is_flag=True, + help='Allow an explicit downgrading version to be given', +) @click.pass_obj -def version(app: Application, desired_version: str | None): +def version(app: Application, *, desired_version: str | None, force: bool): """View or set a project's version.""" if app.project.root is None: if app.project.chosen_name is None: @@ -26,6 +32,7 @@ def version(app: Application, desired_version: str | None): app.display(app.project.metadata.config['project']['version']) return + from hatch.config.constants import VersionEnvVars from hatch.project.constants import BUILD_BACKEND with app.project.location.as_cwd(): @@ -40,33 +47,18 @@ def version(app: Application, desired_version: str | None): project_metadata = app.project.build_frontend.get_core_metadata() app.display(project_metadata['version']) - elif 'version' not in app.project.metadata.dynamic: - source = app.project.metadata.hatch.version.source - - version_data = source.get_version_data() - original_version = version_data['version'] - - if not desired_version: - app.display(original_version) - return - - updated_version = app.project.metadata.hatch.version.scheme.update( - desired_version, original_version, version_data - ) - source.set_version(updated_version, version_data) - - app.display_info(f'Old: {original_version}') - app.display_info(f'New: {updated_version}') else: from hatch.utils.runner import ExecutionContext app.ensure_environment_plugin_dependencies() app.project.prepare_build_environment() + context = ExecutionContext(app.project.build_env) command = ['python', '-u', '-m', 'hatchling', 'version'] if desired_version: command.append(desired_version) + if force: + context.env_vars[VersionEnvVars.VALIDATE_BUMP] = 'false' - context = ExecutionContext(app.project.build_env) context.add_shell_command(command) app.execute_context(context) diff --git a/src/hatch/config/constants.py b/src/hatch/config/constants.py index 2ad442fe1..2f2f73ee3 100644 --- a/src/hatch/config/constants.py +++ b/src/hatch/config/constants.py @@ -33,3 +33,7 @@ class PythonEnvVars: CUSTOM_SOURCE_PREFIX = 'HATCH_PYTHON_CUSTOM_SOURCE_' CUSTOM_PATH_PREFIX = 'HATCH_PYTHON_CUSTOM_PATH_' CUSTOM_VERSION_PREFIX = 'HATCH_PYTHON_CUSTOM_VERSION_' + + +class VersionEnvVars: + VALIDATE_BUMP = 'HATCH_VERSION_VALIDATE_BUMP' diff --git a/tests/backend/version/scheme/test_standard.py b/tests/backend/version/scheme/test_standard.py index 545e6bfeb..fc58123ce 100644 --- a/tests/backend/version/scheme/test_standard.py +++ b/tests/backend/version/scheme/test_standard.py @@ -1,6 +1,8 @@ import pytest from packaging.version import _parse_letter_version # noqa: PLC2701 +from hatch.utils.structures import EnvVars +from hatchling.utils.constants import VersionEnvVars from hatchling.version.scheme.standard import StandardScheme @@ -17,12 +19,19 @@ def test_specific(isolation): assert scheme.update('9000.0.0-rc.1', '1.0', {}) == '9000.0.0rc1' -def test_specific_not_higher_allowed(isolation): +def test_specific_not_higher_allowed_config(isolation): scheme = StandardScheme(str(isolation), {'validate-bump': False}) assert scheme.update('0.24.4', '1.0.0.dev0', {}) == '0.24.4' +def test_specific_not_higher_allowed_env_var(isolation): + scheme = StandardScheme(str(isolation), {}) + + with EnvVars({VersionEnvVars.VALIDATE_BUMP: 'false'}): + assert scheme.update('0.24.4', '1.0.0.dev0', {}) == '0.24.4' + + def test_release(isolation): scheme = StandardScheme(str(isolation), {}) diff --git a/tests/cli/version/test_version.py b/tests/cli/version/test_version.py index c13e5f27f..d4eb0c419 100644 --- a/tests/cli/version/test_version.py +++ b/tests/cli/version/test_version.py @@ -275,6 +275,51 @@ def test_set_dynamic(hatch, helpers, temp_dir): ) +@pytest.mark.usefixtures('mock_backend_process') +def test_set_dynamic_downgrade(hatch, helpers, temp_dir): + project_name = 'My.App' + + with temp_dir.as_cwd(): + hatch('new', project_name) + + path = temp_dir / 'my-app' + data_path = temp_dir / 'data' + data_path.mkdir() + + (path / 'src' / 'my_app' / '__about__.py').write_text('__version__ = "21.1.2"') + + # This one fails, because it's a downgrade without --force + with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): + result = hatch('version', '21.1.0', catch_exceptions=True) + + assert result.exit_code == 1, result.output + assert str(result.exception) == 'Version `21.1.0` is not higher than the original version `21.1.2`' + + # Try again, this time with --force + with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): + result = hatch('version', '--force', '21.1.0') + + assert result.exit_code == 0, result.output + assert result.output == helpers.dedent( + """ + Inspecting build dependencies + Old: 21.1.2 + New: 21.1.0 + """ + ) + + with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): + result = hatch('version') + + assert result.exit_code == 0, result.output + assert result.output == helpers.dedent( + """ + Inspecting build dependencies + 21.1.0 + """ + ) + + def test_show_static(hatch, temp_dir): project_name = 'My.App' From 5352e4422636cf1238017a74f0c67d689ccee558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Tue, 3 Sep 2024 12:27:36 -0600 Subject: [PATCH 05/16] Use `include-hidden-files: true` to upload coverage artifacts (#1707) --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 49c5dfb66..67d0864a5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,6 +57,7 @@ jobs: - name: Upload coverage data uses: actions/upload-artifact@v4 with: + include-hidden-files: true name: coverage-${{ matrix.os }}-${{ matrix.python-version }} path: .coverage* From 28bfe354ff23e387a40126b0ead18e3f0e9e3751 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 13 Oct 2024 18:56:07 -0400 Subject: [PATCH 06/16] 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 07/16] 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 08/16] 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 09/16] 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 10/16] 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 11/16] 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 12/16] 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 13/16] 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 14/16] 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 15/16] 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 From 58c0982d0325608e3ca8c68c392d94c98585c889 Mon Sep 17 00:00:00 2001 From: Eitan <49152796+eitanV81@users.noreply.github.com> Date: Sun, 10 Nov 2024 00:09:06 +0200 Subject: [PATCH 16/16] Enable Zip64 Support for Large Wheel Files in Reproducible Mode (#1576) Co-authored-by: Ofek Lev --- backend/src/hatchling/builders/wheel.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/hatchling/builders/wheel.py b/backend/src/hatchling/builders/wheel.py index 7ef38a1bf..e6bc55262 100644 --- a/backend/src/hatchling/builders/wheel.py +++ b/backend/src/hatchling/builders/wheel.py @@ -102,6 +102,8 @@ def add_file(self, included_file: IncludedFile) -> tuple[str, str, str]: set_zip_info_mode(zip_info, new_mode) if stat.S_ISDIR(file_stat.st_mode): # no cov zip_info.external_attr |= 0x10 + else: + zip_info.file_size = file_stat.st_size else: zip_info = zipfile.ZipInfo.from_file(included_file.path, relative_path)