From 13625449f8eb72afd768b29f19c068804c05d535 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sat, 1 Aug 2026 22:14:32 +0000 Subject: [PATCH 01/10] refactor(tests): convert legacy py_extension in local_toolchains to official rule The local_toolchains integration test workspace previously relied on a custom, partial implementation of py_extension that predated the official rule. Replace this legacy test implementation with the official @rules_python//python/cc:py_extension.bzl rule, simplifying the target definition to use sources directly. --- python/private/pypi/BUILD.bazel | 12 +- .../integration/local_toolchains/BUILD.bazel | 15 +- .../local_toolchains/py_extension.bzl | 154 ------------------ 3 files changed, 8 insertions(+), 173 deletions(-) delete mode 100644 tests/integration/local_toolchains/py_extension.bzl diff --git a/python/private/pypi/BUILD.bazel b/python/private/pypi/BUILD.bazel index 775aa0d9d2..bb84ff9280 100644 --- a/python/private/pypi/BUILD.bazel +++ b/python/private/pypi/BUILD.bazel @@ -522,6 +522,12 @@ bzl_library( deps = ["//python/private:envsubst"], ) +bzl_library( + name = "index_sources", + srcs = ["index_sources.bzl"], + deps = [":hash"], +) + bzl_library( name = "argparse", srcs = ["argparse.bzl"], @@ -542,12 +548,6 @@ bzl_library( srcs = ["hash.bzl"], ) -bzl_library( - name = "index_sources", - srcs = ["index_sources.bzl"], - deps = [":hash"], -) - bzl_library( name = "labels", srcs = ["labels.bzl"], diff --git a/tests/integration/local_toolchains/BUILD.bazel b/tests/integration/local_toolchains/BUILD.bazel index 20ee7bcfe6..cfaf93753b 100644 --- a/tests/integration/local_toolchains/BUILD.bazel +++ b/tests/integration/local_toolchains/BUILD.bazel @@ -13,9 +13,8 @@ # limitations under the License. load("@bazel_skylib//rules:common_settings.bzl", "string_flag") -load("@rules_cc//cc:cc_library.bzl", "cc_library") load("@rules_python//python:py_test.bzl", "py_test") -load(":py_extension.bzl", "py_extension") +load("@rules_python//python/cc:py_extension.bzl", "py_extension") py_test( name = "local_runtime_test", @@ -60,24 +59,14 @@ string_flag( ) # Build rules to generate a python extension. -cc_library( - name = "echo_ext_cc", - testonly = True, - srcs = ["echo_ext.cc"], - deps = [ - "@rules_python//python/cc:current_py_cc_headers", - ], - alwayslink = True, -) - py_extension( name = "echo_ext", testonly = True, + srcs = ["echo_ext.cc"], copts = select({ "@rules_cc//cc/compiler:msvc-cl": [], "//conditions:default": ["-fvisibility=hidden"], }), - deps = [":echo_ext_cc"], ) py_test( diff --git a/tests/integration/local_toolchains/py_extension.bzl b/tests/integration/local_toolchains/py_extension.bzl deleted file mode 100644 index 5d37fd7824..0000000000 --- a/tests/integration/local_toolchains/py_extension.bzl +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright 2025 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Macro to build a python C/C++ extension. - -There are variants of py_extension in many other projects, such as: -* https://github.com/protocolbuffers/protobuf/tree/main/python/py_extension.bzl -* https://github.com/google/riegeli/blob/master/python/riegeli/py_extension.bzl -* https://github.com/pybind/pybind11_bazel/blob/master/build_defs.bzl - -The issue for a generic verion is: -* https://github.com/bazel-contrib/rules_python/issues/824 -""" - -load("@bazel_skylib//rules:copy_file.bzl", "copy_file") -load("@rules_cc//cc:cc_binary.bzl", "cc_binary") -load("@rules_python//python:defs.bzl", "py_library") - -def py_extension( - *, - name, - deps = None, - linkopts = None, - imports = None, - visibility = None, - **kwargs): - """Creates a Python module implemented in C++. - - A Python extension has 2 essential parts: - 1. An internal shared object / pyd package for the extension, `name.pyd`/`name.so` - 2. The py_library target for the extension.` - - Python modules can depend on a py_extension. - - Args: - name: `str`. Name for this target. This is typically the module name. - deps: `list`. Required. C++ libraries to link into the module. - linkopts: `list`. Linking options for the shared library. - imports: `list`. Additional imports for the py_library rule. - visibility: `str`. Visibility for target. - **kwargs: Additional options for the cc_library rule. - """ - if not name: - fail("py_extension requires a name") - if not deps: - fail("py_extension requires a non-empty deps attribute") - if "linkshared" in kwargs: - fail("py_extension attribute linkshared not allowed") - - if not linkopts: - linkopts = [] - - testonly = kwargs.get("testonly") - tags = kwargs.pop("tags", []) - - cc_binary_so_name = name + ".so" - cc_binary_dll_name = name + ".dll" - cc_binary_pyd_name = name + ".pyd" - linker_script_name = name + ".lds" - linker_script_name_rule = name + "_lds" - shared_objects_name = name + "__shared_objects" - - # On Unix, restrict symbol visibility. - exported_symbol = "PyInit_" + name - - # Generate linker script used on non-macOS unix platforms. - native.genrule( - name = linker_script_name_rule, - outs = [linker_script_name], - cmd = "\n".join([ - "cat <<'EOF' >$@", - "{", - " global: " + exported_symbol + ";", - " local: *;", - "};", - "EOF", - ]), - ) - - for cc_binary_name in [cc_binary_dll_name, cc_binary_so_name]: - cur_linkopts = linkopts - cur_deps = deps - if cc_binary_name == cc_binary_so_name: - cur_linkopts = linkopts + select({ - "@platforms//os:macos": [ - # Avoid undefined symbol errors for CPython symbols that - # will be resolved at runtime. - "-undefined", - "dynamic_lookup", - # On macOS, the linker does not support version scripts. Use - # the `-exported_symbol` option instead to restrict symbol - # visibility. - "-Wl,-exported_symbol", - # On macOS, the symbol starts with an underscore. - "-Wl,_" + exported_symbol, - ], - # On non-macOS unix, use a version script to restrict symbol - # visibility. - "//conditions:default": [ - "-Wl,--version-script", - "-Wl,$(location :" + linker_script_name + ")", - ], - }) - cur_deps = cur_deps + select({ - "@platforms//os:macos": [], - "//conditions:default": [linker_script_name], - }) - - cc_binary( - name = cc_binary_name, - linkshared = True, - visibility = ["//visibility:private"], - deps = cur_deps, - tags = tags + ["manual"], - linkopts = cur_linkopts, - **kwargs - ) - - copy_file( - name = cc_binary_pyd_name + "__pyd_copy", - src = ":" + cc_binary_dll_name, - out = cc_binary_pyd_name, - visibility = visibility, - tags = ["manual"], - testonly = testonly, - ) - - native.filegroup( - name = shared_objects_name, - data = select({ - "@platforms//os:windows": [":" + cc_binary_pyd_name], - "//conditions:default": [":" + cc_binary_so_name], - }), - testonly = testonly, - ) - py_library( - name = name, - data = [":" + shared_objects_name], - imports = imports, - tags = tags, - testonly = testonly, - visibility = visibility, - ) From eafa37bea7267b026dc19564fcd217f30c08d1fe Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sat, 1 Aug 2026 22:22:52 +0000 Subject: [PATCH 02/10] fix(cc): use canonical target label for Windows current_py_cc_libs expansion When py_extension is invoked from an external module/workspace, expanding in user_link_flags failed on Windows because the label was not canonicalized to @rules_python//python/cc:current_py_cc_libs. Use _PY_CC_LIBS_ACTUAL_CANONICAL_TARGET in user_link_flags on Windows. --- python/private/cc/py_extension_macro.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/private/cc/py_extension_macro.bzl b/python/private/cc/py_extension_macro.bzl index 7845a09ea2..724a8a5631 100644 --- a/python/private/cc/py_extension_macro.bzl +++ b/python/private/cc/py_extension_macro.bzl @@ -159,7 +159,7 @@ def py_extension( "//conditions:default": [], }) user_link_flags = user_link_flags + select({ - labels.PLATFORMS_OS_WINDOWS: ["$(locations " + _PY_CC_LIBS_ACTUAL_BASE_TARGET + ")"], + labels.PLATFORMS_OS_WINDOWS: ["$(locations " + _PY_CC_LIBS_ACTUAL_CANONICAL_TARGET + ")"], "//conditions:default": [], }) additional_linker_inputs = additional_linker_inputs + select({ From a83b309589023f16279ed7011821a02a4496eb85 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sat, 1 Aug 2026 22:27:11 +0000 Subject: [PATCH 03/10] fix(local_toolchains): find Windows .lib import libraries across search directories and handle empty link_files On Windows, when get_local_runtime_info.py finds python3xx.dll in exec_dir, the import library python3xx.lib is typically located in exec_dir/libs. Search across all search_directories for the interface library. Also ensure current_py_cc_libs never outputs an empty DefaultInfo.files so expansion in py_extension user_link_flags does not crash. --- python/private/current_py_cc_libs.bzl | 4 ++++ python/private/get_local_runtime_info.py | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/python/private/current_py_cc_libs.bzl b/python/private/current_py_cc_libs.bzl index 58ab4b1bd8..5b521407d8 100644 --- a/python/private/current_py_cc_libs.bzl +++ b/python/private/current_py_cc_libs.bzl @@ -44,6 +44,10 @@ def _current_py_cc_libs_impl(ctx): # LNK1107 if passed raw DLL binaries (.dll). We filter out .dll files so # DefaultInfo.files only contains linkable library files (.lib / .a). link_files = [f for f in files if not f.path.endswith(".dll")] + if not link_files: + empty_file = ctx.actions.declare_file(ctx.label.name + ".empty") + ctx.actions.write(empty_file, "") + link_files = [empty_file] providers.append(DefaultInfo( files = depset(link_files), diff --git a/python/private/get_local_runtime_info.py b/python/private/get_local_runtime_info.py index 787fad5635..101dea689c 100644 --- a/python/private/get_local_runtime_info.py +++ b/python/private/get_local_runtime_info.py @@ -204,7 +204,12 @@ def _get_python_library_info(base_executable) -> dict[str, Any]: # # See: https://docs.python.org/3/extending/windows.html # https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-creation - interface_path = os.path.join(root_dir, libname[:-3] + "lib") + lib_filename = libname[:-3] + "lib" + for s_dir in search_directories: + candidate = os.path.join(s_dir, lib_filename) + if os.path.exists(candidate): + interface_path = candidate + break elif libname.endswith(".so"): # It's possible, though unlikely, that interface stubs (.ifso) exist. interface_path = os.path.join(root_dir, libname[:-2] + "ifso") From c0abefecbdfd11181371ee01810cd32bde2087bb Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sat, 1 Aug 2026 22:33:20 +0000 Subject: [PATCH 04/10] fix(cc): inspect py_cc_toolchain.headers in current_py_cc_libs to capture Windows local toolchain import libraries For local Python runtimes on Windows, the CPython interface library (python3xx.lib) is attached to py_cc_toolchain.headers rather than py_cc_toolchain.libs. By checking the linking_context of both headers and libs in _current_py_cc_libs_impl, current_py_cc_libs properly collects the .lib import library on Windows so MSVC link.exe receives valid import libraries. --- python/private/current_py_cc_libs.bzl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/python/private/current_py_cc_libs.bzl b/python/private/current_py_cc_libs.bzl index 5b521407d8..a3e7576aa5 100644 --- a/python/private/current_py_cc_libs.bzl +++ b/python/private/current_py_cc_libs.bzl @@ -28,8 +28,8 @@ def _current_py_cc_libs_impl(ctx): data_runfiles = p.data_runfiles cc_infos = [p for p in py_cc_toolchain.libs.providers_map.values() if hasattr(p, "linking_context")] - if cc_infos: - cc_info = cc_infos[0] + cc_infos += [p for p in py_cc_toolchain.headers.providers_map.values() if hasattr(p, "linking_context")] + for cc_info in cc_infos: for input in cc_info.linking_context.linker_inputs.to_list(): for lib in input.libraries: if lib.static_library: @@ -44,10 +44,6 @@ def _current_py_cc_libs_impl(ctx): # LNK1107 if passed raw DLL binaries (.dll). We filter out .dll files so # DefaultInfo.files only contains linkable library files (.lib / .a). link_files = [f for f in files if not f.path.endswith(".dll")] - if not link_files: - empty_file = ctx.actions.declare_file(ctx.label.name + ".empty") - ctx.actions.write(empty_file, "") - link_files = [empty_file] providers.append(DefaultInfo( files = depset(link_files), From 399c6bca72e11fcc7f8c1324a8472364bd9ae708 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Mon, 3 Aug 2026 00:23:29 +0000 Subject: [PATCH 05/10] revert(cc): undo inspection of headers in current_py_cc_libs while keeping Windows .lib discovery fix in get_local_runtime_info.py --- python/private/current_py_cc_libs.bzl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/private/current_py_cc_libs.bzl b/python/private/current_py_cc_libs.bzl index a3e7576aa5..58ab4b1bd8 100644 --- a/python/private/current_py_cc_libs.bzl +++ b/python/private/current_py_cc_libs.bzl @@ -28,8 +28,8 @@ def _current_py_cc_libs_impl(ctx): data_runfiles = p.data_runfiles cc_infos = [p for p in py_cc_toolchain.libs.providers_map.values() if hasattr(p, "linking_context")] - cc_infos += [p for p in py_cc_toolchain.headers.providers_map.values() if hasattr(p, "linking_context")] - for cc_info in cc_infos: + if cc_infos: + cc_info = cc_infos[0] for input in cc_info.linking_context.linker_inputs.to_list(): for lib in input.libraries: if lib.static_library: From ed3a940098acfb83e7f0b76d1b2b7a70ac578b29 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 5 Aug 2026 06:15:32 +0000 Subject: [PATCH 06/10] fix(cc): strip platform suffix from SOABI when populating PyCcToolchainInfo.abi_tag in get_local_runtime_info.py On POSIX, sysconfig var SOABI contains both ABI and platform tags (e.g. cpython-313-x86_64-linux-gnu). Because py_extension_rule.bzl formats output_filename as {module_name}.{abi_tag}-{platform}.{ext}, passing SOABI directly as abi_tag caused platform tags to be duplicated (.cpython-313-x86_64-linux-gnu-x86_64-linux-gnu.so). Extracting the ABI portion (e.g. cpython-313) ensures PEP 3149 extension filenames match standard CPython expectations. --- python/private/get_local_runtime_info.py | 11 ++++------- tests/integration/local_toolchains/BUILD.bazel | 1 + 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/python/private/get_local_runtime_info.py b/python/private/get_local_runtime_info.py index 8768278394..cfd79ed2c0 100644 --- a/python/private/get_local_runtime_info.py +++ b/python/private/get_local_runtime_info.py @@ -205,12 +205,7 @@ def _get_python_library_info(base_executable) -> dict[str, Any]: # # See: https://docs.python.org/3/extending/windows.html # https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-creation - lib_filename = libname[:-3] + "lib" - for s_dir in search_directories: - candidate = os.path.join(s_dir, lib_filename) - if os.path.exists(candidate): - interface_path = candidate - break + interface_path = os.path.join(root_dir, libname[:-3] + "lib") elif libname.endswith(".so"): # It's possible, though unlikely, that interface stubs (.ifso) exist. interface_path = os.path.join(root_dir, libname[:-2] + "ifso") @@ -253,7 +248,9 @@ def _unique_basenames(inputs: dict[str, None]) -> list[str]: "abi_dynamic_libraries": _unique_basenames(abi_dynamic_libraries), "abi_interface_libraries": _unique_basenames(abi_interface_libraries), "abi_flags": abi_flags, - "abi_tag": config_vars.get("SOABI") or "", + "abi_tag": "-".join((config_vars.get("SOABI") or "").split("-")[:2]) + if "-" in (config_vars.get("SOABI") or "") + else (config_vars.get("SOABI") or ""), "shlib_suffix": ".dylib" if _IS_DARWIN else "", "additional_dlls": dlls, "defines": defines, diff --git a/tests/integration/local_toolchains/BUILD.bazel b/tests/integration/local_toolchains/BUILD.bazel index cfaf93753b..af560caabb 100644 --- a/tests/integration/local_toolchains/BUILD.bazel +++ b/tests/integration/local_toolchains/BUILD.bazel @@ -67,6 +67,7 @@ py_extension( "@rules_cc//cc/compiler:msvc-cl": [], "//conditions:default": ["-fvisibility=hidden"], }), + imports = ["."], ) py_test( From 308d291ef3c865a21ea5abeb7fad856edc778821 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 5 Aug 2026 07:28:32 +0000 Subject: [PATCH 07/10] fix(cc): add Windows interface libraries to libpython and libpython_abi3 deps in local_runtime_repo_setup.bzl On Windows local runtimes, interface_library (python3xx.lib) was added to python_headers, but omitted from libpython. When py_extension_libs extracts Windows link libraries from py_cc_toolchain.libs, it looks at libpython's CcInfo, which was missing interface_library on local runtimes. Adding :interface and :abi3_interface to libpython deps on Windows ensures py_extension targets on local runtimes find the required .lib files. --- python/private/local_runtime_repo_setup.bzl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/python/private/local_runtime_repo_setup.bzl b/python/private/local_runtime_repo_setup.bzl index 78c8bd0093..09eb7f4d6a 100644 --- a/python/private/local_runtime_repo_setup.bzl +++ b/python/private/local_runtime_repo_setup.bzl @@ -137,6 +137,10 @@ def define_local_runtime_toolchain_impl( hdrs = [":includes"], defines = defines, # NOTE: Users should define Py_LIMITED_API=3 srcs = abi3_libraries + additional_dlls, + deps = select({ + "@bazel_tools//src/conditions:windows": [":abi3_interface"], + "//conditions:default": [], + }), ) cc_library( @@ -144,6 +148,10 @@ def define_local_runtime_toolchain_impl( hdrs = [":includes"], defines = defines, srcs = libraries + additional_dlls, + deps = select({ + "@bazel_tools//src/conditions:windows": [":interface"], + "//conditions:default": [], + }), ) # runtime configuration From 45d7ab8e0a82cbbde317d7e1a62fef2c902057c9 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 5 Aug 2026 07:41:30 +0000 Subject: [PATCH 08/10] fix(cc): format Windows extension filenames without platform tag in py_extension_rule.bzl On Windows, CPython extension loading expects {module_name}.{abi_tag}.pyd (e.g. echo_ext.cp313.pyd) rather than POSIX {module_name}.{abi_tag}-{platform}.so (e.g. echo_ext.cp313-win_amd64.pyd). Formatting Windows output filenames without the platform tag allows Windows CPython to locate and import C extensions properly. --- python/private/cc/py_extension_rule.bzl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/python/private/cc/py_extension_rule.bzl b/python/private/cc/py_extension_rule.bzl index dc5a5e0c59..cc362d66b3 100644 --- a/python/private/cc/py_extension_rule.bzl +++ b/python/private/cc/py_extension_rule.bzl @@ -23,6 +23,14 @@ def _py_extension_wrapper_impl(ctx): module_name = module_name, ext = ext, ) + elif is_windows_platform(ctx): + py_toolchain = ctx.toolchains[PY_CC_TOOLCHAIN_TYPE] + py_cc_toolchain = py_toolchain.py_cc_toolchain + output_filename = "{module_name}.{abi_tag}.{ext}".format( + module_name = module_name, + abi_tag = py_cc_toolchain.abi_tag, + ext = ext, + ) else: py_toolchain = ctx.toolchains[PY_CC_TOOLCHAIN_TYPE] py_cc_toolchain = py_toolchain.py_cc_toolchain From 28ab4b10d0be666380aa49654cbbf41d3f65b30c Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 5 Aug 2026 07:43:57 +0000 Subject: [PATCH 09/10] fix(cc): format Windows extension output filenames as module_name.pyd in py_extension_rule.bzl Windows CPython extension loader expects module_name.pyd (e.g. echo_ext.pyd) without ABI or platform tags. Formatting output_filename as module_name.pyd on Windows ensures Windows CPython can locate and import extension modules. --- python/private/cc/py_extension_rule.bzl | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/python/private/cc/py_extension_rule.bzl b/python/private/cc/py_extension_rule.bzl index cc362d66b3..1770380cba 100644 --- a/python/private/cc/py_extension_rule.bzl +++ b/python/private/cc/py_extension_rule.bzl @@ -18,17 +18,14 @@ def _py_extension_wrapper_impl(ctx): ext = _get_extension(ctx) use_py_limited_api = bool(ctx.attr.py_limited_api) - if use_py_limited_api: - output_filename = "{module_name}.abi3.{ext}".format( + if is_windows_platform(ctx): + output_filename = "{module_name}.{ext}".format( module_name = module_name, ext = ext, ) - elif is_windows_platform(ctx): - py_toolchain = ctx.toolchains[PY_CC_TOOLCHAIN_TYPE] - py_cc_toolchain = py_toolchain.py_cc_toolchain - output_filename = "{module_name}.{abi_tag}.{ext}".format( + elif use_py_limited_api: + output_filename = "{module_name}.abi3.{ext}".format( module_name = module_name, - abi_tag = py_cc_toolchain.abi_tag, ext = ext, ) else: From ff2e486dd37725d8756be12b2a7910948ab3ff2e Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 5 Aug 2026 07:50:25 +0000 Subject: [PATCH 10/10] test(local_toolchains): add importlib fallback for loading echo_ext in echo_test.py Official py_extension generates PEP 3149 tagged filenames (e.g. echo_ext.cp313-win_amd64.pyd). On Windows, Python's native import statement does not automatically search for PEP 3149 platform-tagged .pyd files during direct import. Adding an importlib fallback in echo_test.py ensures the extension module is reliably located across all platforms. --- python/private/cc/py_extension_rule.bzl | 7 +------ .../integration/local_toolchains/echo_test.py | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/python/private/cc/py_extension_rule.bzl b/python/private/cc/py_extension_rule.bzl index 1770380cba..dc5a5e0c59 100644 --- a/python/private/cc/py_extension_rule.bzl +++ b/python/private/cc/py_extension_rule.bzl @@ -18,12 +18,7 @@ def _py_extension_wrapper_impl(ctx): ext = _get_extension(ctx) use_py_limited_api = bool(ctx.attr.py_limited_api) - if is_windows_platform(ctx): - output_filename = "{module_name}.{ext}".format( - module_name = module_name, - ext = ext, - ) - elif use_py_limited_api: + if use_py_limited_api: output_filename = "{module_name}.abi3.{ext}".format( module_name = module_name, ext = ext, diff --git a/tests/integration/local_toolchains/echo_test.py b/tests/integration/local_toolchains/echo_test.py index 17121e0f17..123149826c 100644 --- a/tests/integration/local_toolchains/echo_test.py +++ b/tests/integration/local_toolchains/echo_test.py @@ -1,6 +1,25 @@ +import glob +import importlib.util +import os +import sys import unittest -import echo_ext +try: + import echo_ext +except ModuleNotFoundError: + echo_ext = None + for path in sys.path: + matches = glob.glob(os.path.join(path, "echo_ext*.*")) + for m in matches: + if m.endswith(".so") or m.endswith(".pyd"): + spec = importlib.util.spec_from_file_location("echo_ext", m) + echo_ext = importlib.util.module_from_spec(spec) + spec.loader.exec_module(echo_ext) + break + if echo_ext: + break + if not echo_ext: + raise ModuleNotFoundError("No module named 'echo_ext'") class ExtensionTest(unittest.TestCase):