diff --git a/distutils/command/build_ext.py b/distutils/command/build_ext.py index ec45b440..66ae7eb9 100644 --- a/distutils/command/build_ext.py +++ b/distutils/command/build_ext.py @@ -728,23 +728,32 @@ def get_ext_filename(self, ext_name: str) -> str: def get_export_symbols(self, ext: Extension) -> list[str]: """Return the list of symbols that a shared extension has to - export. This either uses 'ext.export_symbols' or, if it's not - provided, "PyInit_" + module_name. Only relevant on Windows, where - the .pyd file (DLL) must export the module "PyInit_" function. + export. This returns, and possibly updates, 'ext.export_symbols'. + + On Python 3.14 and below (that is, before a new export hook name was added), + it adds "PyInit_" + module_name to 'ext.export_symbols'. + Only relevant on Windows, where the .pyd file (DLL) must export the module + import hook function. + + Since Python 3.15, don't add anything. + An export directive was added to the "PyMODINIT_FUNC" implementation + for Python 3.15, which is all that is needed. """ - name = self._get_module_name_for_symbol(ext) - try: - # Unicode module name support as defined in PEP-489 - # https://peps.python.org/pep-0489/#export-hook-name - name.encode('ascii') - except UnicodeEncodeError: - suffix = 'U_' + name.encode('punycode').replace(b'-', b'_').decode('ascii') - else: - suffix = "_" + name + if sys.version_info < (3, 15): + name = self._get_module_name_for_symbol(ext) + try: + # Unicode module name support as defined in PEP-489 + # https://peps.python.org/pep-0489/#export-hook-name + name.encode('ascii') + except UnicodeEncodeError: + name_bytes = name.encode('punycode').replace(b'-', b'_') + suffix = 'U_' + name_bytes.decode('ascii') + else: + suffix = "_" + name - initfunc_name = "PyInit" + suffix - if initfunc_name not in ext.export_symbols: - ext.export_symbols.append(initfunc_name) + initfunc_name = "PyInit" + suffix + if initfunc_name not in ext.export_symbols: + ext.export_symbols.append(initfunc_name) return ext.export_symbols def _get_module_name_for_symbol(self, ext): diff --git a/distutils/tests/test_build_ext.py b/distutils/tests/test_build_ext.py index dab0507f..bdaea0bf 100644 --- a/distutils/tests/test_build_ext.py +++ b/distutils/tests/test_build_ext.py @@ -397,8 +397,12 @@ def test_unicode_module_names(self): cmd.ensure_finalized() assert re.search(r'foo(_d)?\..*', cmd.get_ext_filename(modules[0].name)) assert re.search(r'föö(_d)?\..*', cmd.get_ext_filename(modules[1].name)) - assert cmd.get_export_symbols(modules[0]) == ['PyInit_foo'] - assert cmd.get_export_symbols(modules[1]) == ['PyInitU_f_1gaa'] + if sys.version_info < (3, 15): + assert cmd.get_export_symbols(modules[0]) == ['PyInit_foo'] + assert cmd.get_export_symbols(modules[1]) == ['PyInitU_f_1gaa'] + else: + assert cmd.get_export_symbols(modules[0]) == [] + assert cmd.get_export_symbols(modules[1]) == [] def test_export_symbols__init__(self): # https://github.com/python/cpython/issues/80074 @@ -410,8 +414,12 @@ def test_export_symbols__init__(self): dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = self.build_ext(dist) cmd.ensure_finalized() - assert cmd.get_export_symbols(modules[0]) == ['PyInit_foo'] - assert cmd.get_export_symbols(modules[1]) == ['PyInitU_f_1gaa'] + if sys.version_info < (3, 15): + assert cmd.get_export_symbols(modules[0]) == ['PyInit_foo'] + assert cmd.get_export_symbols(modules[1]) == ['PyInitU_f_1gaa'] + else: + assert cmd.get_export_symbols(modules[0]) == [] + assert cmd.get_export_symbols(modules[1]) == [] def test_compiler_option(self): # cmd.compiler is an option and