From 0426e9a9290afed8fa7b563aff9f116dd2ce784b Mon Sep 17 00:00:00 2001 From: "M. Eric Irrgang" Date: Wed, 10 Dec 2025 16:13:52 -0800 Subject: [PATCH 1/5] Establish a new test structure for different resource layouts. --- tests/packages/cmake_generated/CMakeLists.txt | 6 ++ tests/packages/cmake_generated/pyproject.toml | 24 +++++ .../src/cmake_generated/__init__.py | 47 +++++++++ .../cmake_generated/namespace1/static_data | 1 + .../src/cmake_generated/nested1/__init__.py | 4 + .../src/cmake_generated/nested1/static_data | 1 + .../src/cmake_generated/static_data | 1 + tests/test_editable_generated.py | 98 +++++++++++++++++++ 8 files changed, 182 insertions(+) create mode 100644 tests/packages/cmake_generated/CMakeLists.txt create mode 100644 tests/packages/cmake_generated/pyproject.toml create mode 100644 tests/packages/cmake_generated/src/cmake_generated/__init__.py create mode 100644 tests/packages/cmake_generated/src/cmake_generated/namespace1/static_data create mode 100644 tests/packages/cmake_generated/src/cmake_generated/nested1/__init__.py create mode 100644 tests/packages/cmake_generated/src/cmake_generated/nested1/static_data create mode 100644 tests/packages/cmake_generated/src/cmake_generated/static_data create mode 100644 tests/test_editable_generated.py diff --git a/tests/packages/cmake_generated/CMakeLists.txt b/tests/packages/cmake_generated/CMakeLists.txt new file mode 100644 index 000000000..8e0696655 --- /dev/null +++ b/tests/packages/cmake_generated/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.15) + +project( + ${SKBUILD_PROJECT_NAME} + LANGUAGES CXX + VERSION 1.2.3) diff --git a/tests/packages/cmake_generated/pyproject.toml b/tests/packages/cmake_generated/pyproject.toml new file mode 100644 index 000000000..3244b4906 --- /dev/null +++ b/tests/packages/cmake_generated/pyproject.toml @@ -0,0 +1,24 @@ +[build-system] +requires = ["scikit-build-core"] +build-backend = "scikit_build_core.build" + +[project] +name = "cmake_generated" +dynamic = ["version"] + +[tool.scikit-build] +# Bundling a generated file in the sdist is not supported at this time. +# sdist.cmake = false +wheel.license-files = [] +wheel.exclude = ["**.cpp", "**.in"] + +[tool.scikit-build.metadata.version] +provider = "scikit_build_core.metadata.regex" +input = "CMakeLists.txt" +regex = 'project\([^)]+ VERSION (?P[0-9.]+)' + +[[tool.scikit-build.generate]] +path = "cmake_generated/_version.py" +template = ''' +version = "${version}" +''' diff --git a/tests/packages/cmake_generated/src/cmake_generated/__init__.py b/tests/packages/cmake_generated/src/cmake_generated/__init__.py new file mode 100644 index 000000000..a0036db3c --- /dev/null +++ b/tests/packages/cmake_generated/src/cmake_generated/__init__.py @@ -0,0 +1,47 @@ +"""Package that includes several non-Python non-module files. + +Support some test cases aimed at testing our ability to find generated files +and static package data files in editable installations. + +We are exercising the importlib machinery to find files that +are generated in different phases of the build and in different parts of +the package layout to check that the redirection works correctly in an +editable installation. + +The test package includes raw data files and shared object libraries that +are accessed via `ctypes`. + +We test files (generated and static) + +* at the top level of the package, +* in subpackages, and +* in a namespace package. + +We test access + +* from modules at the same level as the files, +* one level above and below, and +* from parallel subpackages. + +Question: Do we want to test both relative and absolute imports or just one or the other? +""" +from importlib.resources import files + +try: + from ._version import __version__ +except ImportError: + __version__ = None + +def get_static_data(): + return files().joinpath("static_data").read_text().rstrip() + +# Access importlib resources as early as possible in the import process to check +# for edge cases with import hooks. +static_data = get_static_data() + +# Check consistency of various access modes (or describe unsupported access modes). +assert files("cmake_generated").joinpath("static_data").read_text().rstrip() == static_data + +def get_namespace_static_data(): + # Ref https://github.com/python/importlib_resources/issues/262 + return files().joinpath("namespace1").joinpath("static_data").read_text().rstrip() diff --git a/tests/packages/cmake_generated/src/cmake_generated/namespace1/static_data b/tests/packages/cmake_generated/src/cmake_generated/namespace1/static_data new file mode 100644 index 000000000..2e114a4b6 --- /dev/null +++ b/tests/packages/cmake_generated/src/cmake_generated/namespace1/static_data @@ -0,0 +1 @@ +static value in namespace package diff --git a/tests/packages/cmake_generated/src/cmake_generated/nested1/__init__.py b/tests/packages/cmake_generated/src/cmake_generated/nested1/__init__.py new file mode 100644 index 000000000..5c8086115 --- /dev/null +++ b/tests/packages/cmake_generated/src/cmake_generated/nested1/__init__.py @@ -0,0 +1,4 @@ +from importlib.resources import files + +def get_static_data(): + return files().joinpath("static_data").read_text().rstrip() diff --git a/tests/packages/cmake_generated/src/cmake_generated/nested1/static_data b/tests/packages/cmake_generated/src/cmake_generated/nested1/static_data new file mode 100644 index 000000000..bdc4abc1d --- /dev/null +++ b/tests/packages/cmake_generated/src/cmake_generated/nested1/static_data @@ -0,0 +1 @@ +static value in subpackage 1 diff --git a/tests/packages/cmake_generated/src/cmake_generated/static_data b/tests/packages/cmake_generated/src/cmake_generated/static_data new file mode 100644 index 000000000..6d0400337 --- /dev/null +++ b/tests/packages/cmake_generated/src/cmake_generated/static_data @@ -0,0 +1 @@ +static value in top-level package diff --git a/tests/test_editable_generated.py b/tests/test_editable_generated.py new file mode 100644 index 000000000..b65e3dccc --- /dev/null +++ b/tests/test_editable_generated.py @@ -0,0 +1,98 @@ +"""Test regular and editable installs with generated files. + +Illustrate the supported and correct ways to use generated files +(other than traditional compiled extension modules). + +Check a variety of scenarios in which package files (modules or data) are +not present in the source tree to confirm that we can find resources as expected, +either by ``import`` or with tools such as `importlib.resources.files()`. +""" +from __future__ import annotations + +import sys +from pathlib import Path + +import pytest +from conftest import PackageInfo, VEnv, process_package + +def _setup_package_for_editable_layout_tests( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + editable: bool, + editable_mode: str, + build_isolation: bool, + isolated: VEnv, +) -> None: + editable_flag = ["-e"] if editable else [] + + config_mode_flags = [] + if editable: + config_mode_flags.append(f"--config-settings=editable.mode={editable_mode}") + if editable_mode != "inplace": + config_mode_flags.append("--config-settings=build-dir=build/{wheel_tag}") + + build_isolation_flags = [] + if not build_isolation: + build_isolation_flags.append("--no-build-isolation") + + # Use a context so that we only change into the directory up until the point where + # we run the editable install. We do not want to be in that directory when importing + # to avoid importing the source directory instead of the installed package. + with monkeypatch.context() as m: + package = PackageInfo("cmake_generated") + process_package(package, tmp_path, m) + + assert isolated.wheelhouse + + ninja = [ + "ninja" + for f in isolated.wheelhouse.iterdir() + if f.name.startswith("ninja-") + ] + cmake = [ + "cmake" + for f in isolated.wheelhouse.iterdir() + if f.name.startswith("cmake-") + ] + + isolated.install("pip>23") + isolated.install("scikit-build-core", *ninja, *cmake) + + isolated.install( + "-v", + *config_mode_flags, + *build_isolation_flags, + *editable_flag, + ".", + ) + + +@pytest.mark.compile +@pytest.mark.configure +@pytest.mark.integration +@pytest.mark.parametrize( + ("editable", "editable_mode"), + [ + (False, ""), + (True, "redirect"), + (True, "inplace"), + ], +) +@pytest.mark.parametrize( + "build_isolation", + [True, False], +) +@pytest.mark.skipif(sys.version_info < (3, 9), reason="importlib.resources.files is introduced in Python 3.9") +def test_basic_data_resources(monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated): + _setup_package_for_editable_layout_tests( + monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated + ) + + value = isolated.execute("import cmake_generated; print(cmake_generated.get_static_data())") + assert value == "static value in top-level package" + + value = isolated.execute("import cmake_generated.nested1; print(cmake_generated.nested1.get_static_data())") + assert value == "static value in subpackage 1" + + value = isolated.execute("import cmake_generated; print(cmake_generated.get_namespace_static_data())") + assert value == "static value in namespace package" From 7bdec1e905d91ac8568f326d4c0c660ef1f1c68b Mon Sep 17 00:00:00 2001 From: "M. Eric Irrgang" Date: Wed, 10 Dec 2025 16:32:13 -0800 Subject: [PATCH 2/5] Check that a compiled binary can be used via ctypes --- tests/packages/cmake_generated/CMakeLists.txt | 13 ++++++++++ .../src/cmake_generated/__init__.py | 14 ++++++++++- .../src/cmake_generated/pkg.cpp | 1 + tests/test_editable_generated.py | 24 +++++++++++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/packages/cmake_generated/src/cmake_generated/pkg.cpp diff --git a/tests/packages/cmake_generated/CMakeLists.txt b/tests/packages/cmake_generated/CMakeLists.txt index 8e0696655..f181c7087 100644 --- a/tests/packages/cmake_generated/CMakeLists.txt +++ b/tests/packages/cmake_generated/CMakeLists.txt @@ -4,3 +4,16 @@ project( ${SKBUILD_PROJECT_NAME} LANGUAGES CXX VERSION 1.2.3) + +add_library(pkg MODULE src/cmake_generated/pkg.cpp) + +if(NOT WIN32) + # Explicitly set the bundle extension to .so + set_target_properties(pkg PROPERTIES + SUFFIX ".so" + ) +endif() + +# Set the library name to "pkg", regardless of the OS convention. +set_target_properties(pkg PROPERTIES PREFIX "") +install(TARGETS pkg DESTINATION ${SKBUILD_PROJECT_NAME}) diff --git a/tests/packages/cmake_generated/src/cmake_generated/__init__.py b/tests/packages/cmake_generated/src/cmake_generated/__init__.py index a0036db3c..6377c7939 100644 --- a/tests/packages/cmake_generated/src/cmake_generated/__init__.py +++ b/tests/packages/cmake_generated/src/cmake_generated/__init__.py @@ -25,7 +25,9 @@ Question: Do we want to test both relative and absolute imports or just one or the other? """ -from importlib.resources import files +import ctypes +import sys +from importlib.resources import files, as_file try: from ._version import __version__ @@ -45,3 +47,13 @@ def get_static_data(): def get_namespace_static_data(): # Ref https://github.com/python/importlib_resources/issues/262 return files().joinpath("namespace1").joinpath("static_data").read_text().rstrip() + +def ctypes_function(): + # Question: can anyone think of a clever way to embed the actual library name in some other package metadata? + if sys.platform == "win32": + lib_suffix = "dll" + else: + lib_suffix = "so" + with as_file(files().joinpath(f"pkg.{lib_suffix}")) as lib_path: + lib = ctypes.cdll.LoadLibrary(str(lib_path)) + return lib.func diff --git a/tests/packages/cmake_generated/src/cmake_generated/pkg.cpp b/tests/packages/cmake_generated/src/cmake_generated/pkg.cpp new file mode 100644 index 000000000..9572bf146 --- /dev/null +++ b/tests/packages/cmake_generated/src/cmake_generated/pkg.cpp @@ -0,0 +1 @@ +extern "C" int func() {return 42;} diff --git a/tests/test_editable_generated.py b/tests/test_editable_generated.py index b65e3dccc..2fe25b7da 100644 --- a/tests/test_editable_generated.py +++ b/tests/test_editable_generated.py @@ -96,3 +96,27 @@ def test_basic_data_resources(monkeypatch, tmp_path, editable, editable_mode, bu value = isolated.execute("import cmake_generated; print(cmake_generated.get_namespace_static_data())") assert value == "static value in namespace package" + +@pytest.mark.compile +@pytest.mark.configure +@pytest.mark.integration +@pytest.mark.parametrize( + ("editable", "editable_mode"), + [ + (False, ""), + (True, "redirect"), + (True, "inplace"), + ], +) +@pytest.mark.parametrize( + "build_isolation", + [True, False], +) +@pytest.mark.skipif(sys.version_info < (3, 9), reason="importlib.resources.files is introduced in Python 3.9") +def test_compiled_ctypes_resource(monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated): + _setup_package_for_editable_layout_tests( + monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated + ) + + value = isolated.execute("import cmake_generated; print(cmake_generated.ctypes_function()())") + assert value == str(42) From 1ce8a5508c0449c75f30fb9adbb8e08ed7188074 Mon Sep 17 00:00:00 2001 From: "M. Eric Irrgang" Date: Thu, 11 Dec 2025 12:45:46 -0800 Subject: [PATCH 3/5] Check other generated file use cases. --- tests/packages/cmake_generated/CMakeLists.txt | 32 +++++ tests/packages/cmake_generated/pyproject.toml | 2 +- .../src/cmake_generated/__init__.py | 19 +-- .../src/cmake_generated/nested1/__init__.py | 4 +- .../cmake_generated/nested1/generated.py.in | 27 ++++ .../src/cmake_generated/nested2/__init__.py | 4 + tests/test_editable_generated.py | 120 ++++++++++++++++++ 7 files changed, 196 insertions(+), 12 deletions(-) create mode 100644 tests/packages/cmake_generated/src/cmake_generated/nested1/generated.py.in create mode 100644 tests/packages/cmake_generated/src/cmake_generated/nested2/__init__.py diff --git a/tests/packages/cmake_generated/CMakeLists.txt b/tests/packages/cmake_generated/CMakeLists.txt index f181c7087..0b0ed6b3b 100644 --- a/tests/packages/cmake_generated/CMakeLists.txt +++ b/tests/packages/cmake_generated/CMakeLists.txt @@ -5,6 +5,38 @@ project( LANGUAGES CXX VERSION 1.2.3) +# Generate files at config time (configure_file) and at build time (add_custom_command) +# Note that bundling a generated file with sdist is out of scope for now. +# Note: cmake_generated/nested1/generated.py should try to open both generated and static files. +configure_file(src/cmake_generated/nested1/generated.py.in generated.py) +# We always expect the install phase to run, so the build tree layout can be +# different than the package layout. +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/generated.py DESTINATION ${SKBUILD_PROJECT_NAME}/nested1) + +file(GENERATE + OUTPUT configured_file + CONTENT "value written by cmake file generation") +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/configured_file DESTINATION ${SKBUILD_PROJECT_NAME}) + +set(OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/generated_data") +set(FILE_CONTENT "value written by cmake custom_command") +set(GENERATE_SCRIPT + "file(WRITE \"${OUTPUT_FILE}\" \"${FILE_CONTENT}\")" +) +add_custom_command( + OUTPUT "${OUTPUT_FILE}" + COMMAND "${CMAKE_COMMAND}" + -P "${CMAKE_CURRENT_BINARY_DIR}/generate_file.cmake" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/generate_file.cmake" + COMMENT "Generating ${OUTPUT_FILE} using CMake scripting at build time" + VERBATIM +) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/generate_file.cmake" "${GENERATE_SCRIPT}") +add_custom_target(generate_file ALL + DEPENDS "${OUTPUT_FILE}" +) +install(FILES "${OUTPUT_FILE}" DESTINATION ${SKBUILD_PROJECT_NAME}/namespace1) + add_library(pkg MODULE src/cmake_generated/pkg.cpp) if(NOT WIN32) diff --git a/tests/packages/cmake_generated/pyproject.toml b/tests/packages/cmake_generated/pyproject.toml index 3244b4906..da2c8081d 100644 --- a/tests/packages/cmake_generated/pyproject.toml +++ b/tests/packages/cmake_generated/pyproject.toml @@ -20,5 +20,5 @@ regex = 'project\([^)]+ VERSION (?P[0-9.]+)' [[tool.scikit-build.generate]] path = "cmake_generated/_version.py" template = ''' -version = "${version}" +__version__ = "${version}" ''' diff --git a/tests/packages/cmake_generated/src/cmake_generated/__init__.py b/tests/packages/cmake_generated/src/cmake_generated/__init__.py index 6377c7939..4d9dd2959 100644 --- a/tests/packages/cmake_generated/src/cmake_generated/__init__.py +++ b/tests/packages/cmake_generated/src/cmake_generated/__init__.py @@ -27,7 +27,7 @@ """ import ctypes import sys -from importlib.resources import files, as_file +from importlib.resources import files, as_file, read_text try: from ._version import __version__ @@ -35,18 +35,19 @@ __version__ = None def get_static_data(): - return files().joinpath("static_data").read_text().rstrip() + return read_text("cmake_generated", "static_data").rstrip() -# Access importlib resources as early as possible in the import process to check -# for edge cases with import hooks. -static_data = get_static_data() - -# Check consistency of various access modes (or describe unsupported access modes). -assert files("cmake_generated").joinpath("static_data").read_text().rstrip() == static_data +def get_configured_data(): + return files().joinpath("configured_file").read_text().rstrip() def get_namespace_static_data(): + # read_text is able to handle a namespace subpackage directly, though `files()` is not. + return read_text("cmake_generated.namespace1","static_data").rstrip() + +def get_namespace_generated_data(): + # Note that `files("cmake_generated.namespace1")` doesn't work. # Ref https://github.com/python/importlib_resources/issues/262 - return files().joinpath("namespace1").joinpath("static_data").read_text().rstrip() + return files().joinpath("namespace1").joinpath("generated_data").read_text().rstrip() def ctypes_function(): # Question: can anyone think of a clever way to embed the actual library name in some other package metadata? diff --git a/tests/packages/cmake_generated/src/cmake_generated/nested1/__init__.py b/tests/packages/cmake_generated/src/cmake_generated/nested1/__init__.py index 5c8086115..2c7383fe8 100644 --- a/tests/packages/cmake_generated/src/cmake_generated/nested1/__init__.py +++ b/tests/packages/cmake_generated/src/cmake_generated/nested1/__init__.py @@ -1,4 +1,4 @@ -from importlib.resources import files +from importlib.resources import read_text def get_static_data(): - return files().joinpath("static_data").read_text().rstrip() + return read_text("cmake_generated.nested1","static_data").rstrip() diff --git a/tests/packages/cmake_generated/src/cmake_generated/nested1/generated.py.in b/tests/packages/cmake_generated/src/cmake_generated/nested1/generated.py.in new file mode 100644 index 000000000..f39f510be --- /dev/null +++ b/tests/packages/cmake_generated/src/cmake_generated/nested1/generated.py.in @@ -0,0 +1,27 @@ +"""Try to open both generated and static files from various parts of the package.""" +from importlib.resources import files, read_text + +from .. import __version__ + +try: + from .. import nested2 +except ImportError: + nested2 = None + +def cmake_generated_static_data(): + return read_text("cmake_generated", "static_data").rstrip() + +def cmake_generated_nested_static_data(): + return files("cmake_generated.nested1").joinpath("static_data").read_text().rstrip() + +def get_configured_data(): + return files("cmake_generated").joinpath("configured_file").read_text().rstrip() + +def cmake_generated_namespace_generated_data(): + return read_text("cmake_generated.namespace1","generated_data").rstrip() + +nested_data = "success" + +def nested2_check(): + if nested2 is not None: + return "success" diff --git a/tests/packages/cmake_generated/src/cmake_generated/nested2/__init__.py b/tests/packages/cmake_generated/src/cmake_generated/nested2/__init__.py new file mode 100644 index 000000000..6024d6bdb --- /dev/null +++ b/tests/packages/cmake_generated/src/cmake_generated/nested2/__init__.py @@ -0,0 +1,4 @@ +def nested1_generated_check(): + # noinspection PyUnresolvedReferences + from ..nested1.generated import nested_data + return nested_data diff --git a/tests/test_editable_generated.py b/tests/test_editable_generated.py index 2fe25b7da..d55f5d179 100644 --- a/tests/test_editable_generated.py +++ b/tests/test_editable_generated.py @@ -97,6 +97,57 @@ def test_basic_data_resources(monkeypatch, tmp_path, editable, editable_mode, bu value = isolated.execute("import cmake_generated; print(cmake_generated.get_namespace_static_data())") assert value == "static value in namespace package" + +@pytest.mark.compile +@pytest.mark.configure +@pytest.mark.integration +@pytest.mark.parametrize( + ("editable", "editable_mode"), + [ + (False, ""), + (True, "redirect"), + (True, "inplace"), + ], +) +@pytest.mark.parametrize( + "build_isolation", + [True, False], +) +@pytest.mark.skipif(sys.version_info < (3, 9), reason="importlib.resources.files is introduced in Python 3.9") +def test_configure_time_generated_data(monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated): + _setup_package_for_editable_layout_tests( + monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated + ) + + value = isolated.execute("import cmake_generated; print(cmake_generated.get_configured_data())") + assert value == "value written by cmake file generation" + + +@pytest.mark.compile +@pytest.mark.configure +@pytest.mark.integration +@pytest.mark.parametrize( + ("editable", "editable_mode"), + [ + (False, ""), + (True, "redirect"), + (True, "inplace"), + ], +) +@pytest.mark.parametrize( + "build_isolation", + [True, False], +) +@pytest.mark.skipif(sys.version_info < (3, 9), reason="importlib.resources.files is introduced in Python 3.9") +def test_build_time_generated_data(monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated): + _setup_package_for_editable_layout_tests( + monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated + ) + + value = isolated.execute("import cmake_generated; print(cmake_generated.get_namespace_generated_data())") + assert value == "value written by cmake custom_command" + + @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration @@ -120,3 +171,72 @@ def test_compiled_ctypes_resource(monkeypatch, tmp_path, editable, editable_mode value = isolated.execute("import cmake_generated; print(cmake_generated.ctypes_function()())") assert value == str(42) + + +@pytest.mark.compile +@pytest.mark.configure +@pytest.mark.integration +@pytest.mark.parametrize( + ("editable", "editable_mode"), + [ + (False, ""), + (True, "redirect"), + (True, "inplace"), + ], +) +@pytest.mark.parametrize( + "build_isolation", + [True, False], +) +@pytest.mark.skipif(sys.version_info < (3, 9), reason="importlib.resources.files is introduced in Python 3.9") +def test_configure_time_generated_module(monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated): + _setup_package_for_editable_layout_tests( + monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated + ) + # Check that a generated module can access and be accessed by all parts of the package + + value = isolated.execute("from cmake_generated.nested1.generated import __version__; print(__version__)") + assert value == "1.2.3" + + value = isolated.execute("from cmake_generated.nested1.generated import cmake_generated_static_data; print(cmake_generated_static_data())") + assert value == "static value in top-level package" + + value = isolated.execute("from cmake_generated.nested1.generated import cmake_generated_nested_static_data; print(cmake_generated_nested_static_data())") + assert value == "static value in subpackage 1" + + value = isolated.execute("from cmake_generated.nested1.generated import cmake_generated_namespace_generated_data; print(cmake_generated_namespace_generated_data())") + assert value == "value written by cmake custom_command" + + value = isolated.execute("from cmake_generated.nested1.generated import nested_data; print(nested_data)") + assert value == "success" + value = isolated.execute("from cmake_generated.nested1.generated import nested2_check; print(nested2_check())") + assert value == "success" + value = isolated.execute("from cmake_generated.nested2 import nested1_generated_check; print(nested1_generated_check())") + assert value == "success" + + +@pytest.mark.compile +@pytest.mark.configure +@pytest.mark.integration +@pytest.mark.parametrize( + ("editable", "editable_mode"), + [ + (False, ""), + (True, "redirect"), + (True, "inplace"), + ], +) +@pytest.mark.parametrize( + "build_isolation", + [True, False], +) +@pytest.mark.skipif(sys.version_info < (3, 9), reason="importlib.resources.files is introduced in Python 3.9") +def test_build_time_generated_module(monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated): + _setup_package_for_editable_layout_tests( + monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated + ) + # Check generated _version module + attr_value = isolated.execute("import cmake_generated; print(cmake_generated.__version__)") + assert attr_value == "1.2.3" + metadata_value = isolated.execute("import importlib.metadata; print(importlib.metadata.version('cmake_generated'))") + assert metadata_value == "1.2.3" From d280d760fe23e55bd11256d9bc058a7436e613b8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 21:02:48 +0000 Subject: [PATCH 4/5] style: pre-commit fixes --- tests/packages/cmake_generated/CMakeLists.txt | 44 ++++--- .../src/cmake_generated/__init__.py | 14 ++- .../src/cmake_generated/nested1/__init__.py | 3 +- .../src/cmake_generated/nested2/__init__.py | 1 + tests/test_editable_generated.py | 116 ++++++++++++++---- 5 files changed, 124 insertions(+), 54 deletions(-) diff --git a/tests/packages/cmake_generated/CMakeLists.txt b/tests/packages/cmake_generated/CMakeLists.txt index 0b0ed6b3b..6fa55eec7 100644 --- a/tests/packages/cmake_generated/CMakeLists.txt +++ b/tests/packages/cmake_generated/CMakeLists.txt @@ -5,45 +5,43 @@ project( LANGUAGES CXX VERSION 1.2.3) -# Generate files at config time (configure_file) and at build time (add_custom_command) -# Note that bundling a generated file with sdist is out of scope for now. -# Note: cmake_generated/nested1/generated.py should try to open both generated and static files. +# Generate files at config time (configure_file) and at build time +# (add_custom_command) Note that bundling a generated file with sdist is out of +# scope for now. Note: cmake_generated/nested1/generated.py should try to open +# both generated and static files. configure_file(src/cmake_generated/nested1/generated.py.in generated.py) # We always expect the install phase to run, so the build tree layout can be # different than the package layout. -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/generated.py DESTINATION ${SKBUILD_PROJECT_NAME}/nested1) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/generated.py + DESTINATION ${SKBUILD_PROJECT_NAME}/nested1) -file(GENERATE - OUTPUT configured_file - CONTENT "value written by cmake file generation") -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/configured_file DESTINATION ${SKBUILD_PROJECT_NAME}) +file( + GENERATE + OUTPUT configured_file + CONTENT "value written by cmake file generation") +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/configured_file + DESTINATION ${SKBUILD_PROJECT_NAME}) set(OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/generated_data") set(FILE_CONTENT "value written by cmake custom_command") -set(GENERATE_SCRIPT - "file(WRITE \"${OUTPUT_FILE}\" \"${FILE_CONTENT}\")" -) +set(GENERATE_SCRIPT "file(WRITE \"${OUTPUT_FILE}\" \"${FILE_CONTENT}\")") add_custom_command( OUTPUT "${OUTPUT_FILE}" - COMMAND "${CMAKE_COMMAND}" - -P "${CMAKE_CURRENT_BINARY_DIR}/generate_file.cmake" + COMMAND "${CMAKE_COMMAND}" -P + "${CMAKE_CURRENT_BINARY_DIR}/generate_file.cmake" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/generate_file.cmake" COMMENT "Generating ${OUTPUT_FILE} using CMake scripting at build time" - VERBATIM -) -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/generate_file.cmake" "${GENERATE_SCRIPT}") -add_custom_target(generate_file ALL - DEPENDS "${OUTPUT_FILE}" -) + VERBATIM) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/generate_file.cmake" + "${GENERATE_SCRIPT}") +add_custom_target(generate_file ALL DEPENDS "${OUTPUT_FILE}") install(FILES "${OUTPUT_FILE}" DESTINATION ${SKBUILD_PROJECT_NAME}/namespace1) add_library(pkg MODULE src/cmake_generated/pkg.cpp) if(NOT WIN32) - # Explicitly set the bundle extension to .so - set_target_properties(pkg PROPERTIES - SUFFIX ".so" - ) + # Explicitly set the bundle extension to .so + set_target_properties(pkg PROPERTIES SUFFIX ".so") endif() # Set the library name to "pkg", regardless of the OS convention. diff --git a/tests/packages/cmake_generated/src/cmake_generated/__init__.py b/tests/packages/cmake_generated/src/cmake_generated/__init__.py index 4d9dd2959..c43b05f30 100644 --- a/tests/packages/cmake_generated/src/cmake_generated/__init__.py +++ b/tests/packages/cmake_generated/src/cmake_generated/__init__.py @@ -25,29 +25,37 @@ Question: Do we want to test both relative and absolute imports or just one or the other? """ + import ctypes import sys -from importlib.resources import files, as_file, read_text +from importlib.resources import as_file, files, read_text try: from ._version import __version__ except ImportError: __version__ = None + def get_static_data(): return read_text("cmake_generated", "static_data").rstrip() + def get_configured_data(): return files().joinpath("configured_file").read_text().rstrip() + def get_namespace_static_data(): # read_text is able to handle a namespace subpackage directly, though `files()` is not. - return read_text("cmake_generated.namespace1","static_data").rstrip() + return read_text("cmake_generated.namespace1", "static_data").rstrip() + def get_namespace_generated_data(): # Note that `files("cmake_generated.namespace1")` doesn't work. # Ref https://github.com/python/importlib_resources/issues/262 - return files().joinpath("namespace1").joinpath("generated_data").read_text().rstrip() + return ( + files().joinpath("namespace1").joinpath("generated_data").read_text().rstrip() + ) + def ctypes_function(): # Question: can anyone think of a clever way to embed the actual library name in some other package metadata? diff --git a/tests/packages/cmake_generated/src/cmake_generated/nested1/__init__.py b/tests/packages/cmake_generated/src/cmake_generated/nested1/__init__.py index 2c7383fe8..71741dd57 100644 --- a/tests/packages/cmake_generated/src/cmake_generated/nested1/__init__.py +++ b/tests/packages/cmake_generated/src/cmake_generated/nested1/__init__.py @@ -1,4 +1,5 @@ from importlib.resources import read_text + def get_static_data(): - return read_text("cmake_generated.nested1","static_data").rstrip() + return read_text("cmake_generated.nested1", "static_data").rstrip() diff --git a/tests/packages/cmake_generated/src/cmake_generated/nested2/__init__.py b/tests/packages/cmake_generated/src/cmake_generated/nested2/__init__.py index 6024d6bdb..ac9993e74 100644 --- a/tests/packages/cmake_generated/src/cmake_generated/nested2/__init__.py +++ b/tests/packages/cmake_generated/src/cmake_generated/nested2/__init__.py @@ -1,4 +1,5 @@ def nested1_generated_check(): # noinspection PyUnresolvedReferences from ..nested1.generated import nested_data + return nested_data diff --git a/tests/test_editable_generated.py b/tests/test_editable_generated.py index d55f5d179..4403aede6 100644 --- a/tests/test_editable_generated.py +++ b/tests/test_editable_generated.py @@ -7,6 +7,7 @@ not present in the source tree to confirm that we can find resources as expected, either by ``import`` or with tools such as `importlib.resources.files()`. """ + from __future__ import annotations import sys @@ -15,6 +16,7 @@ import pytest from conftest import PackageInfo, VEnv, process_package + def _setup_package_for_editable_layout_tests( monkeypatch: pytest.MonkeyPatch, tmp_path: Path, @@ -82,19 +84,30 @@ def _setup_package_for_editable_layout_tests( "build_isolation", [True, False], ) -@pytest.mark.skipif(sys.version_info < (3, 9), reason="importlib.resources.files is introduced in Python 3.9") -def test_basic_data_resources(monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated): +@pytest.mark.skipif( + sys.version_info < (3, 9), + reason="importlib.resources.files is introduced in Python 3.9", +) +def test_basic_data_resources( + monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated +): _setup_package_for_editable_layout_tests( monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated ) - value = isolated.execute("import cmake_generated; print(cmake_generated.get_static_data())") + value = isolated.execute( + "import cmake_generated; print(cmake_generated.get_static_data())" + ) assert value == "static value in top-level package" - value = isolated.execute("import cmake_generated.nested1; print(cmake_generated.nested1.get_static_data())") + value = isolated.execute( + "import cmake_generated.nested1; print(cmake_generated.nested1.get_static_data())" + ) assert value == "static value in subpackage 1" - value = isolated.execute("import cmake_generated; print(cmake_generated.get_namespace_static_data())") + value = isolated.execute( + "import cmake_generated; print(cmake_generated.get_namespace_static_data())" + ) assert value == "static value in namespace package" @@ -113,13 +126,20 @@ def test_basic_data_resources(monkeypatch, tmp_path, editable, editable_mode, bu "build_isolation", [True, False], ) -@pytest.mark.skipif(sys.version_info < (3, 9), reason="importlib.resources.files is introduced in Python 3.9") -def test_configure_time_generated_data(monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated): +@pytest.mark.skipif( + sys.version_info < (3, 9), + reason="importlib.resources.files is introduced in Python 3.9", +) +def test_configure_time_generated_data( + monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated +): _setup_package_for_editable_layout_tests( monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated ) - value = isolated.execute("import cmake_generated; print(cmake_generated.get_configured_data())") + value = isolated.execute( + "import cmake_generated; print(cmake_generated.get_configured_data())" + ) assert value == "value written by cmake file generation" @@ -138,13 +158,20 @@ def test_configure_time_generated_data(monkeypatch, tmp_path, editable, editable "build_isolation", [True, False], ) -@pytest.mark.skipif(sys.version_info < (3, 9), reason="importlib.resources.files is introduced in Python 3.9") -def test_build_time_generated_data(monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated): +@pytest.mark.skipif( + sys.version_info < (3, 9), + reason="importlib.resources.files is introduced in Python 3.9", +) +def test_build_time_generated_data( + monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated +): _setup_package_for_editable_layout_tests( monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated ) - value = isolated.execute("import cmake_generated; print(cmake_generated.get_namespace_generated_data())") + value = isolated.execute( + "import cmake_generated; print(cmake_generated.get_namespace_generated_data())" + ) assert value == "value written by cmake custom_command" @@ -163,13 +190,20 @@ def test_build_time_generated_data(monkeypatch, tmp_path, editable, editable_mod "build_isolation", [True, False], ) -@pytest.mark.skipif(sys.version_info < (3, 9), reason="importlib.resources.files is introduced in Python 3.9") -def test_compiled_ctypes_resource(monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated): +@pytest.mark.skipif( + sys.version_info < (3, 9), + reason="importlib.resources.files is introduced in Python 3.9", +) +def test_compiled_ctypes_resource( + monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated +): _setup_package_for_editable_layout_tests( monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated ) - value = isolated.execute("import cmake_generated; print(cmake_generated.ctypes_function()())") + value = isolated.execute( + "import cmake_generated; print(cmake_generated.ctypes_function()())" + ) assert value == str(42) @@ -188,30 +222,49 @@ def test_compiled_ctypes_resource(monkeypatch, tmp_path, editable, editable_mode "build_isolation", [True, False], ) -@pytest.mark.skipif(sys.version_info < (3, 9), reason="importlib.resources.files is introduced in Python 3.9") -def test_configure_time_generated_module(monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated): +@pytest.mark.skipif( + sys.version_info < (3, 9), + reason="importlib.resources.files is introduced in Python 3.9", +) +def test_configure_time_generated_module( + monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated +): _setup_package_for_editable_layout_tests( monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated ) # Check that a generated module can access and be accessed by all parts of the package - value = isolated.execute("from cmake_generated.nested1.generated import __version__; print(__version__)") + value = isolated.execute( + "from cmake_generated.nested1.generated import __version__; print(__version__)" + ) assert value == "1.2.3" - value = isolated.execute("from cmake_generated.nested1.generated import cmake_generated_static_data; print(cmake_generated_static_data())") + value = isolated.execute( + "from cmake_generated.nested1.generated import cmake_generated_static_data; print(cmake_generated_static_data())" + ) assert value == "static value in top-level package" - value = isolated.execute("from cmake_generated.nested1.generated import cmake_generated_nested_static_data; print(cmake_generated_nested_static_data())") + value = isolated.execute( + "from cmake_generated.nested1.generated import cmake_generated_nested_static_data; print(cmake_generated_nested_static_data())" + ) assert value == "static value in subpackage 1" - value = isolated.execute("from cmake_generated.nested1.generated import cmake_generated_namespace_generated_data; print(cmake_generated_namespace_generated_data())") + value = isolated.execute( + "from cmake_generated.nested1.generated import cmake_generated_namespace_generated_data; print(cmake_generated_namespace_generated_data())" + ) assert value == "value written by cmake custom_command" - value = isolated.execute("from cmake_generated.nested1.generated import nested_data; print(nested_data)") + value = isolated.execute( + "from cmake_generated.nested1.generated import nested_data; print(nested_data)" + ) assert value == "success" - value = isolated.execute("from cmake_generated.nested1.generated import nested2_check; print(nested2_check())") + value = isolated.execute( + "from cmake_generated.nested1.generated import nested2_check; print(nested2_check())" + ) assert value == "success" - value = isolated.execute("from cmake_generated.nested2 import nested1_generated_check; print(nested1_generated_check())") + value = isolated.execute( + "from cmake_generated.nested2 import nested1_generated_check; print(nested1_generated_check())" + ) assert value == "success" @@ -230,13 +283,22 @@ def test_configure_time_generated_module(monkeypatch, tmp_path, editable, editab "build_isolation", [True, False], ) -@pytest.mark.skipif(sys.version_info < (3, 9), reason="importlib.resources.files is introduced in Python 3.9") -def test_build_time_generated_module(monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated): +@pytest.mark.skipif( + sys.version_info < (3, 9), + reason="importlib.resources.files is introduced in Python 3.9", +) +def test_build_time_generated_module( + monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated +): _setup_package_for_editable_layout_tests( monkeypatch, tmp_path, editable, editable_mode, build_isolation, isolated ) # Check generated _version module - attr_value = isolated.execute("import cmake_generated; print(cmake_generated.__version__)") + attr_value = isolated.execute( + "import cmake_generated; print(cmake_generated.__version__)" + ) assert attr_value == "1.2.3" - metadata_value = isolated.execute("import importlib.metadata; print(importlib.metadata.version('cmake_generated'))") + metadata_value = isolated.execute( + "import importlib.metadata; print(importlib.metadata.version('cmake_generated'))" + ) assert metadata_value == "1.2.3" From 979a38fb8a69bdd8267b09c348a623564ed25973 Mon Sep 17 00:00:00 2001 From: "M. Eric Irrgang" Date: Mon, 15 Dec 2025 15:44:40 -0800 Subject: [PATCH 5/5] Apply suggestions from code review --- .../cmake_generated/src/cmake_generated/__init__.py | 1 - tests/test_editable_generated.py | 9 +++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/packages/cmake_generated/src/cmake_generated/__init__.py b/tests/packages/cmake_generated/src/cmake_generated/__init__.py index c43b05f30..80081d787 100644 --- a/tests/packages/cmake_generated/src/cmake_generated/__init__.py +++ b/tests/packages/cmake_generated/src/cmake_generated/__init__.py @@ -58,7 +58,6 @@ def get_namespace_generated_data(): def ctypes_function(): - # Question: can anyone think of a clever way to embed the actual library name in some other package metadata? if sys.platform == "win32": lib_suffix = "dll" else: diff --git a/tests/test_editable_generated.py b/tests/test_editable_generated.py index 4403aede6..5825d1efa 100644 --- a/tests/test_editable_generated.py +++ b/tests/test_editable_generated.py @@ -33,10 +33,6 @@ def _setup_package_for_editable_layout_tests( if editable_mode != "inplace": config_mode_flags.append("--config-settings=build-dir=build/{wheel_tag}") - build_isolation_flags = [] - if not build_isolation: - build_isolation_flags.append("--no-build-isolation") - # Use a context so that we only change into the directory up until the point where # we run the editable install. We do not want to be in that directory when importing # to avoid importing the source directory instead of the installed package. @@ -58,14 +54,15 @@ def _setup_package_for_editable_layout_tests( ] isolated.install("pip>23") - isolated.install("scikit-build-core", *ninja, *cmake) + if not build_isolation: + isolated.install("scikit-build-core", *ninja, *cmake) isolated.install( "-v", *config_mode_flags, - *build_isolation_flags, *editable_flag, ".", + isolated=build_isolation, )