diff --git a/.copier-answers.yaml b/.copier-answers.yaml index 046fdfd..df923c8 100644 --- a/.copier-answers.yaml +++ b/.copier-answers.yaml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: ebb6e1c +_commit: '4372075' _src_path: https://github.com/python-project-templates/base.git add_docs: false add_extension: python diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 18e05ce..e3339bc 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -15,7 +15,7 @@ on: workflow_dispatch: concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: diff --git a/Makefile b/Makefile index cf8510d..ec27884 100644 --- a/Makefile +++ b/Makefile @@ -25,16 +25,16 @@ lint-py: ## lint python with ruff python -m ruff format --check hatch_cpp lint-docs: ## lint docs with mdformat and codespell - python -m mdformat --check README.md - python -m codespell_lib README.md + python -m mdformat --check README.md $(wildcard docs/src) + python -m codespell_lib README.md $(wildcard docs/src) fix-py: ## autoformat python code with ruff python -m ruff check --fix hatch_cpp python -m ruff format hatch_cpp fix-docs: ## autoformat docs with mdformat and codespell - python -m mdformat README.md - python -m codespell_lib --write README.md + python -m mdformat README.md $(wildcard docs/src) + python -m codespell_lib --write README.md $(wildcard docs/src) lint: lint-py lint-docs ## run all linters lints: lint @@ -50,9 +50,9 @@ check-dist: ## check python sdist and wheel with check-dist check-dist -v check-types: ## check python types with ty - ty check --python $$(which python) + python -m ty check -checks: check-dist +checks: check-dist check-types # Alias check: checks diff --git a/hatch_cpp/config.py b/hatch_cpp/config.py index ca8b946..124b06a 100644 --- a/hatch_cpp/config.py +++ b/hatch_cpp/config.py @@ -25,7 +25,7 @@ class HatchCppBuildConfig(BaseModel): name: str | None = Field(default=None) libraries: list[HatchCppLibrary] = Field(default_factory=list) cmake: HatchCppCmakeConfiguration | None = Field(default=None) - platform: HatchCppPlatform | None = Field(default_factory=HatchCppPlatform.default) + platform: HatchCppPlatform = Field(default_factory=HatchCppPlatform.default) vcpkg: HatchCppVcpkgConfiguration | None = Field(default_factory=HatchCppVcpkgConfiguration) @model_validator(mode="wrap") @@ -72,7 +72,7 @@ def generate(self): self._active_toolchains.append("vcpkg") else: log.warning("HATCH_CPP_VCPKG=1 set but no vcpkg configuration found; ignoring.") - elif vcpkg_override != "0" and self.vcpkg and Path(self.vcpkg.vcpkg).exists(): + elif vcpkg_override != "0" and self.vcpkg and self.vcpkg.vcpkg and Path(self.vcpkg.vcpkg).exists(): self._active_toolchains.append("vcpkg") if self.libraries: @@ -87,6 +87,7 @@ def generate(self): # Collect toolchain commands if "vcpkg" in self._active_toolchains: + assert self.vcpkg is not None self.commands.extend(self.vcpkg.generate(self)) if "vanilla" in self._active_toolchains: @@ -108,6 +109,7 @@ def generate(self): self.commands.append(f"{compiler} {' '.join(library.sources)} {compile_flags} {link_flags}") if "cmake" in self._active_toolchains: + assert self.cmake is not None self.commands.extend(self.cmake.generate(self)) return self.commands diff --git a/hatch_cpp/plugin.py b/hatch_cpp/plugin.py index 3e4f3c4..4d8a4f2 100644 --- a/hatch_cpp/plugin.py +++ b/hatch_cpp/plugin.py @@ -104,6 +104,7 @@ def initialize(self, version: str, build_data: dict[str, Any]) -> None: all(lib.py_limited_api for lib in build_plan.libraries), ) else: + assert build_plan.cmake is not None build_data["pure_python"] = False machine = platform_machine() version_major = version_info.major diff --git a/hatch_cpp/tests/test_hatch_build.py b/hatch_cpp/tests/test_hatch_build.py index b71e185..4766631 100644 --- a/hatch_cpp/tests/test_hatch_build.py +++ b/hatch_cpp/tests/test_hatch_build.py @@ -1,3 +1,4 @@ +from importlib import import_module from os import listdir from pathlib import Path from shutil import rmtree @@ -41,6 +42,6 @@ def test_hatch_build(self): # import here = Path(__file__).parent / project path.insert(0, str(here)) - import project.extension + extension = import_module("project.extension") - assert project.extension.hello() == "A string" + assert extension.hello() == "A string" diff --git a/hatch_cpp/tests/test_hook_registration.py b/hatch_cpp/tests/test_hook_registration.py index 75dd412..daf8025 100644 --- a/hatch_cpp/tests/test_hook_registration.py +++ b/hatch_cpp/tests/test_hook_registration.py @@ -1,4 +1,5 @@ from hatchling.builders.hooks.plugin.interface import BuildHookInterface +from hatchling.metadata.core import ProjectMetadata from hatch_cpp.hooks import hatch_register_build_hook @@ -10,7 +11,7 @@ def test_registered_build_hook_accepts_hatchling_configuration(tmp_path): root=str(tmp_path), config=config, build_config=None, - metadata=None, + metadata=ProjectMetadata(str(tmp_path), None, {"project": {"name": "project", "version": "0.1.0"}}), directory=str(tmp_path), target_name="wheel", ) diff --git a/hatch_cpp/tests/test_project_pyodide/pyodide_project/extension.pyi b/hatch_cpp/tests/test_project_pyodide/pyodide_project/extension.pyi new file mode 100644 index 0000000..ceb4070 --- /dev/null +++ b/hatch_cpp/tests/test_project_pyodide/pyodide_project/extension.pyi @@ -0,0 +1 @@ +def answer() -> int: ... diff --git a/hatch_cpp/tests/test_projects.py b/hatch_cpp/tests/test_projects.py index 31c54d6..27f467a 100644 --- a/hatch_cpp/tests/test_projects.py +++ b/hatch_cpp/tests/test_projects.py @@ -1,4 +1,5 @@ import contextlib +from importlib import import_module from os import listdir, remove from pathlib import Path from subprocess import check_call @@ -60,6 +61,6 @@ def test_basic(self, project): # import here = Path(__file__).parent / project path.insert(0, str(here)) - import project.extension + extension = import_module("project.extension") - assert project.extension.hello() == "A string" + assert extension.hello() == "A string" diff --git a/hatch_cpp/toolchains/cmake.py b/hatch_cpp/toolchains/cmake.py index bc53568..685a5ef 100644 --- a/hatch_cpp/toolchains/cmake.py +++ b/hatch_cpp/toolchains/cmake.py @@ -3,7 +3,6 @@ from os import environ from pathlib import Path from sys import version_info -from typing import Any from pydantic import BaseModel, Field @@ -33,7 +32,8 @@ class HatchCppCmakeConfiguration(BaseModel): include_flags: dict[str, str | int | float | bool] | None = Field(default=None) - def generate(self, config) -> dict[str, Any]: + def generate(self, config) -> list[str]: + assert self.root is not None commands = [] # Derive prefix diff --git a/hatch_cpp/toolchains/common.py b/hatch_cpp/toolchains/common.py index ba455a7..c92c88a 100644 --- a/hatch_cpp/toolchains/common.py +++ b/hatch_cpp/toolchains/common.py @@ -48,50 +48,56 @@ class HatchCppLibrary(BaseModel, validate_assignment=True): binding: Binding = "cpython" std: str | None = None - include_dirs: list[str] = Field(default_factory=list, alias=AliasChoices("include_dirs", "include-dirs")) - include_dirs_linux: list[str] = Field(default_factory=list, alias=AliasChoices("include_dirs_linux", "include-dirs-linux")) - include_dirs_darwin: list[str] = Field(default_factory=list, alias=AliasChoices("include_dirs_darwin", "include-dirs-darwin")) - include_dirs_win32: list[str] = Field(default_factory=list, alias=AliasChoices("include_dirs_win32", "include-dirs-win32")) + include_dirs: list[str] = Field(default_factory=list, validation_alias=AliasChoices("include_dirs", "include-dirs")) + include_dirs_linux: list[str] = Field(default_factory=list, validation_alias=AliasChoices("include_dirs_linux", "include-dirs-linux")) + include_dirs_darwin: list[str] = Field(default_factory=list, validation_alias=AliasChoices("include_dirs_darwin", "include-dirs-darwin")) + include_dirs_win32: list[str] = Field(default_factory=list, validation_alias=AliasChoices("include_dirs_win32", "include-dirs-win32")) - library_dirs: list[str] = Field(default_factory=list, alias=AliasChoices("library_dirs", "library-dirs")) - library_dirs_linux: list[str] = Field(default_factory=list, alias=AliasChoices("library_dirs_linux", "library-dirs-linux")) - library_dirs_darwin: list[str] = Field(default_factory=list, alias=AliasChoices("library_dirs_darwin", "library-dirs-darwin")) - library_dirs_win32: list[str] = Field(default_factory=list, alias=AliasChoices("library_dirs_win32", "library-dirs-win32")) + library_dirs: list[str] = Field(default_factory=list, validation_alias=AliasChoices("library_dirs", "library-dirs")) + library_dirs_linux: list[str] = Field(default_factory=list, validation_alias=AliasChoices("library_dirs_linux", "library-dirs-linux")) + library_dirs_darwin: list[str] = Field(default_factory=list, validation_alias=AliasChoices("library_dirs_darwin", "library-dirs-darwin")) + library_dirs_win32: list[str] = Field(default_factory=list, validation_alias=AliasChoices("library_dirs_win32", "library-dirs-win32")) libraries: list[str] = Field(default_factory=list) - libraries_linux: list[str] = Field(default_factory=list, alias=AliasChoices("libraries_linux", "libraries-linux")) - libraries_darwin: list[str] = Field(default_factory=list, alias=AliasChoices("libraries_darwin", "libraries-darwin")) - libraries_win32: list[str] = Field(default_factory=list, alias=AliasChoices("libraries_win32", "libraries-win32")) - - extra_compile_args: list[str] = Field(default_factory=list, alias=AliasChoices("extra_compile_args", "extra-compile-args")) - extra_compile_args_linux: list[str] = Field(default_factory=list, alias=AliasChoices("extra_compile_args_linux", "extra-compile-args-linux")) - extra_compile_args_darwin: list[str] = Field(default_factory=list, alias=AliasChoices("extra_compile_args_darwin", "extra-compile-args-darwin")) - extra_compile_args_win32: list[str] = Field(default_factory=list, alias=AliasChoices("extra_compile_args_win32", "extra-compile-args-win32")) - - extra_link_args: list[str] = Field(default_factory=list, alias=AliasChoices("extra_link_args", "extra-link-args")) - extra_link_args_linux: list[str] = Field(default_factory=list, alias=AliasChoices("extra_link_args_linux", "extra-link-args-linux")) - extra_link_args_darwin: list[str] = Field(default_factory=list, alias=AliasChoices("extra_link_args_darwin", "extra-link-args-darwin")) - extra_link_args_win32: list[str] = Field(default_factory=list, alias=AliasChoices("extra_link_args_win32", "extra-link-args-win32")) - - extra_objects: list[str] = Field(default_factory=list, alias=AliasChoices("extra_objects", "extra-objects")) - extra_objects_linux: list[str] = Field(default_factory=list, alias=AliasChoices("extra_objects_linux", "extra-objects-linux")) - extra_objects_darwin: list[str] = Field(default_factory=list, alias=AliasChoices("extra_objects_darwin", "extra-objects-darwin")) - extra_objects_win32: list[str] = Field(default_factory=list, alias=AliasChoices("extra_objects_win32", "extra-objects-win32")) - - define_macros: list[str] = Field(default_factory=list, alias=AliasChoices("define_macros", "define-macros")) - define_macros_linux: list[str] = Field(default_factory=list, alias=AliasChoices("define_macros_linux", "define-macros-linux")) - define_macros_darwin: list[str] = Field(default_factory=list, alias=AliasChoices("define_macros_darwin", "define-macros-darwin")) - define_macros_win32: list[str] = Field(default_factory=list, alias=AliasChoices("define_macros_win32", "define-macros-win32")) - - undef_macros: list[str] = Field(default_factory=list, alias=AliasChoices("undef_macros", "undef-macros")) - undef_macros_linux: list[str] = Field(default_factory=list, alias=AliasChoices("undef_macros_linux", "undef-macros-linux")) - undef_macros_darwin: list[str] = Field(default_factory=list, alias=AliasChoices("undef_macros_darwin", "undef-macros-darwin")) - undef_macros_win32: list[str] = Field(default_factory=list, alias=AliasChoices("undef_macros_win32", "undef-macros-win32")) - - export_symbols: list[str] = Field(default_factory=list, alias=AliasChoices("export_symbols", "export-symbols")) + libraries_linux: list[str] = Field(default_factory=list, validation_alias=AliasChoices("libraries_linux", "libraries-linux")) + libraries_darwin: list[str] = Field(default_factory=list, validation_alias=AliasChoices("libraries_darwin", "libraries-darwin")) + libraries_win32: list[str] = Field(default_factory=list, validation_alias=AliasChoices("libraries_win32", "libraries-win32")) + + extra_compile_args: list[str] = Field(default_factory=list, validation_alias=AliasChoices("extra_compile_args", "extra-compile-args")) + extra_compile_args_linux: list[str] = Field( + default_factory=list, validation_alias=AliasChoices("extra_compile_args_linux", "extra-compile-args-linux") + ) + extra_compile_args_darwin: list[str] = Field( + default_factory=list, validation_alias=AliasChoices("extra_compile_args_darwin", "extra-compile-args-darwin") + ) + extra_compile_args_win32: list[str] = Field( + default_factory=list, validation_alias=AliasChoices("extra_compile_args_win32", "extra-compile-args-win32") + ) + + extra_link_args: list[str] = Field(default_factory=list, validation_alias=AliasChoices("extra_link_args", "extra-link-args")) + extra_link_args_linux: list[str] = Field(default_factory=list, validation_alias=AliasChoices("extra_link_args_linux", "extra-link-args-linux")) + extra_link_args_darwin: list[str] = Field(default_factory=list, validation_alias=AliasChoices("extra_link_args_darwin", "extra-link-args-darwin")) + extra_link_args_win32: list[str] = Field(default_factory=list, validation_alias=AliasChoices("extra_link_args_win32", "extra-link-args-win32")) + + extra_objects: list[str] = Field(default_factory=list, validation_alias=AliasChoices("extra_objects", "extra-objects")) + extra_objects_linux: list[str] = Field(default_factory=list, validation_alias=AliasChoices("extra_objects_linux", "extra-objects-linux")) + extra_objects_darwin: list[str] = Field(default_factory=list, validation_alias=AliasChoices("extra_objects_darwin", "extra-objects-darwin")) + extra_objects_win32: list[str] = Field(default_factory=list, validation_alias=AliasChoices("extra_objects_win32", "extra-objects-win32")) + + define_macros: list[str] = Field(default_factory=list, validation_alias=AliasChoices("define_macros", "define-macros")) + define_macros_linux: list[str] = Field(default_factory=list, validation_alias=AliasChoices("define_macros_linux", "define-macros-linux")) + define_macros_darwin: list[str] = Field(default_factory=list, validation_alias=AliasChoices("define_macros_darwin", "define-macros-darwin")) + define_macros_win32: list[str] = Field(default_factory=list, validation_alias=AliasChoices("define_macros_win32", "define-macros-win32")) + + undef_macros: list[str] = Field(default_factory=list, validation_alias=AliasChoices("undef_macros", "undef-macros")) + undef_macros_linux: list[str] = Field(default_factory=list, validation_alias=AliasChoices("undef_macros_linux", "undef-macros-linux")) + undef_macros_darwin: list[str] = Field(default_factory=list, validation_alias=AliasChoices("undef_macros_darwin", "undef-macros-darwin")) + undef_macros_win32: list[str] = Field(default_factory=list, validation_alias=AliasChoices("undef_macros_win32", "undef-macros-win32")) + + export_symbols: list[str] = Field(default_factory=list, validation_alias=AliasChoices("export_symbols", "export-symbols")) depends: list[str] = Field(default_factory=list) - py_limited_api: str | None = Field(default="", alias=AliasChoices("py_limited_api", "py-limited-api")) + py_limited_api: str | None = Field(default="", validation_alias=AliasChoices("py_limited_api", "py-limited-api")) @field_validator("py_limited_api", mode="before") @classmethod diff --git a/hatch_cpp/toolchains/vcpkg.py b/hatch_cpp/toolchains/vcpkg.py index 17945de..5856e40 100644 --- a/hatch_cpp/toolchains/vcpkg.py +++ b/hatch_cpp/toolchains/vcpkg.py @@ -31,7 +31,7 @@ "arm64-windows-static-md", "wasm32-emscripten", ] -VcpkgPlatformDefaults = { +VcpkgPlatformDefaults: dict[tuple[str, str], VcpkgTriplet] = { ("linux", "x86_64"): "x64-linux", ("linux", "aarch64"): "arm64-linux", ("darwin", "x86_64"): "x64-osx", @@ -65,7 +65,7 @@ def _read_vcpkg_ref_from_gitmodules(vcpkg_root: Path) -> str | None: class HatchCppVcpkgConfiguration(BaseModel): vcpkg: str | None = Field(default="vcpkg.json") - vcpkg_root: Path | None = Field(default=Path("vcpkg")) + vcpkg_root: Path = Field(default=Path("vcpkg")) vcpkg_repo: str | None = Field(default="https://github.com/microsoft/vcpkg.git") vcpkg_triplet: VcpkgTriplet | None = Field(default=None) vcpkg_ref: str | None = Field( diff --git a/pyproject.toml b/pyproject.toml index 8198cf6..5cc3368 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,7 @@ develop = [ "hatchling", "hatch-build>=0.3.2", "mdformat", + "mdformat-frontmatter", "mdformat-tables>=1", "pytest", "pytest-cov", @@ -97,7 +98,7 @@ replace = 'version = "{new_version}"' [tool.coverage.run] branch = false omit = [ - "hatch_cpp/tests/integration/", + "hatch_cpp/tests/integration/*", ] patch = ["subprocess"]