Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 24 additions & 15 deletions distutils/command/build_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
16 changes: 12 additions & 4 deletions distutils/tests/test_build_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Loading