/
niceSOFT
/
python3-hatchling
Обзор
Документация
Войти
/
niceSOFT
/
python3-hatchling
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
tests/backend/builders/test_custom.py
343 строки
10 KB
Dimitri Papadopoulos Orfanos
Upgrade Ruff to 0.13.2 (#1890)
28 сен 2025, 21:20
Не верифицирован
28 сен 2025, 21:20
aff6f39
Код
Авторство
О чём код?
import re import zipfile import pytest from hatchling.builders.custom import CustomBuilder from hatchling.utils.constants import DEFAULT_BUILD_SCRIPT def test_target_config_not_table(isolation): config = {"tool": {"hatch": {"build": {"targets": {"custom": 9000}}}}} with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.custom` must be a table"): CustomBuilder(str(isolation), config=config) def test_no_path(isolation): config = { "tool": { "hatch": { "build": {"targets": {"custom": {"path": ""}}}, }, }, } with pytest.raises(ValueError, match="Option `path` for builder `custom` must not be empty if defined"): CustomBuilder(str(isolation), config=config) def test_path_not_string(isolation): config = {"tool": {"hatch": {"build": {"targets": {"custom": {"path": 3}}}}}} with pytest.raises(TypeError, match="Option `path` for builder `custom` must be a string"): CustomBuilder(str(isolation), config=config) def test_nonexistent(isolation): config = {"tool": {"hatch": {"build": {"targets": {"custom": {"path": "test.py"}}}}}} with pytest.raises(OSError, match="Build script does not exist: test.py"): CustomBuilder(str(isolation), config=config) def test_default(hatch, helpers, temp_dir, config_file): config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() project_name = "My.App" with temp_dir.as_cwd(): result = hatch("new", project_name) assert result.exit_code == 0, result.output project_path = temp_dir / "my-app" config = { "project": {"name": project_name, "dynamic": ["version"]}, "tool": { "hatch": { "version": {"path": "my_app/__about__.py"}, "build": {"targets": {"custom": {}}}, }, }, } file_path = project_path / DEFAULT_BUILD_SCRIPT file_path.write_text( helpers.dedent( """ import os from hatchling.builders.wheel import WheelBuilder def get_builder(): return CustomWheelBuilder class CustomWheelBuilder(WheelBuilder): def build(self, **kwargs): for i, artifact in enumerate(super().build(**kwargs)): build_dir = os.path.dirname(artifact) new_path = os.path.join(build_dir, f'{self.PLUGIN_NAME}-{i}.whl') os.replace(artifact, new_path) yield new_path """ ) ) builder = CustomBuilder(str(project_path), config=config) build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) assert len(artifacts) == 1 expected_artifact = artifacts[0] build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) assert expected_artifact == str(build_path / "custom-0.whl") extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( "wheel.standard_default_license_single", project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) def test_explicit_path(hatch, helpers, temp_dir, config_file): config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() project_name = "My.App" with temp_dir.as_cwd(): result = hatch("new", project_name) assert result.exit_code == 0, result.output project_path = temp_dir / "my-app" config = { "project": {"name": project_name, "dynamic": ["version"]}, "tool": { "hatch": { "version": {"path": "my_app/__about__.py"}, "build": {"targets": {"custom": {"path": f"foo/{DEFAULT_BUILD_SCRIPT}"}}}, }, }, } file_path = project_path / "foo" / DEFAULT_BUILD_SCRIPT file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( """ import os from hatchling.builders.wheel import WheelBuilder def get_builder(): return CustomWheelBuilder class CustomWheelBuilder(WheelBuilder): def build(self, **kwargs): for i, artifact in enumerate(super().build(**kwargs)): build_dir = os.path.dirname(artifact) new_path = os.path.join(build_dir, f'{self.PLUGIN_NAME}-{i}.whl') os.replace(artifact, new_path) yield new_path """ ) ) builder = CustomBuilder(str(project_path), config=config) build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) assert len(artifacts) == 1 expected_artifact = artifacts[0] build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) assert expected_artifact == str(build_path / "custom-0.whl") extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( "wheel.standard_default_license_single", project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) def test_no_subclass(hatch, helpers, temp_dir): project_name = "My.App" with temp_dir.as_cwd(): result = hatch("new", project_name) assert result.exit_code == 0, result.output project_path = temp_dir / "my-app" config = { "project": {"name": project_name, "dynamic": ["version"]}, "tool": { "hatch": { "version": {"path": "my_app/__about__.py"}, "build": {"targets": {"custom": {"path": f"foo/{DEFAULT_BUILD_SCRIPT}"}}}, }, }, } file_path = project_path / "foo" / DEFAULT_BUILD_SCRIPT file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( """ from hatchling.builders.plugin.interface import BuilderInterface foo = None bar = 'baz' class CustomBuilder: pass """ ) ) with ( pytest.raises( ValueError, match=re.escape( f"Unable to find a subclass of `BuilderInterface` in `foo/{DEFAULT_BUILD_SCRIPT}`: {temp_dir}" ), ), project_path.as_cwd(), ): CustomBuilder(str(project_path), config=config) def test_multiple_subclasses(hatch, helpers, temp_dir): project_name = "My.App" with temp_dir.as_cwd(): result = hatch("new", project_name) assert result.exit_code == 0, result.output project_path = temp_dir / "my-app" config = { "project": {"name": project_name, "dynamic": ["version"]}, "tool": { "hatch": { "version": {"path": "my_app/__about__.py"}, "build": {"targets": {"custom": {"path": f"foo/{DEFAULT_BUILD_SCRIPT}"}}}, }, }, } file_path = project_path / "foo" / DEFAULT_BUILD_SCRIPT file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( """ import os from hatchling.builders.wheel import WheelBuilder class CustomWheelBuilder(WheelBuilder): pass """ ) ) with ( pytest.raises( ValueError, match=re.escape( f"Multiple subclasses of `BuilderInterface` found in `foo/{DEFAULT_BUILD_SCRIPT}`, select " f"one by defining a function named `get_builder`: {temp_dir}" ), ), project_path.as_cwd(), ): CustomBuilder(str(project_path), config=config) def test_dynamic_dependencies(hatch, helpers, temp_dir, config_file): config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() project_name = "My.App" with temp_dir.as_cwd(): result = hatch("new", project_name) assert result.exit_code == 0, result.output project_path = temp_dir / "my-app" config = { "project": {"name": project_name, "dynamic": ["version"]}, "tool": { "hatch": { "version": {"path": "my_app/__about__.py"}, "build": { "targets": {"custom": {"dependencies": ["foo"], "hooks": {"custom": {"dependencies": ["bar"]}}}} }, }, }, } file_path = project_path / DEFAULT_BUILD_SCRIPT file_path.write_text( helpers.dedent( """ from hatchling.builders.hooks.plugin.interface import BuildHookInterface from hatchling.builders.wheel import WheelBuilder def get_builder(): return CustomWheelBuilder class CustomWheelBuilder(WheelBuilder): pass class CustomHook(BuildHookInterface): def dependencies(self): return ['baz'] """ ) ) builder = CustomBuilder(str(project_path), config=config) assert builder.config.dependencies == ["foo", "bar", "baz"]