Skip to content

abi3t migration howto: Windows link fails with LNK2001 "unresolved external symbol PyInit_<name>" — export-symbol step is missing #158098

Closed

Description

@sbSteveK

Documentation

Documentation: https://docs.python.org/3.15/howto/abi3t-migration.html

The abi3t migration howto instructs authors to remove PyInit_ and define PyModExport_ instead (PEP 793). For build tools without native abi3t support, the "Setting up the build" note says the extension "will later need to name and tag the resulting extension manually," and "Tagging and distribution" covers those two manual steps: the filename and the wheel tag.

On Windows there is a third manual step that isn't documented: the linker export list. setuptools (no abi3t support yet, see pypa/setuptools#5205) passes /EXPORT:PyInit_ to the MSVC linker. Since the abi3t module no longer defines PyInit_, following the guide leads to a guaranteed link failure:

code

LINK : error LNK2001: unresolved external symbol PyInit__awscrt
_awscrt.cp315t-win_amd64.lib : fatal error LNK1120: 1 unresolved externals
This doesn't reproduce on Linux/macOS (no export lists there), so it only surfaces at the end of an otherwise-successful port. We hit it migrating awscrt (awslabs/aws-crt-python#768) after the Linux and macOS abi3t builds were already green.

Workaround (setuptools):

python

class my_build_ext(setuptools.command.build_ext.build_ext):
    def get_export_symbols(self, ext):
        symbols = super().get_export_symbols(ext)
        if BUILDING_ABI3T:
            symbols = [s.replace('PyInit_', 'PyModExport_') for s in symbols]
        return symbols

Suggested fix: add a sentence to "Tagging and distribution", e.g.:

On Windows, also ensure your build exports PyModExport_ rather than PyInit_; build tools without abi3t support pass /EXPORT:PyInit_ to the linker, which fails because that function no longer exists. With setuptools, override build_ext.get_export_symbols().

Linked PRs

Activity

  1. picnixz commented on Sep 24, 2026

    @picnixz
    Member
  2. encukou commented on Sep 24, 2026

    @encukou
    Member

    Thank you for bringing this up!

    I think this is a bug in setuptools/distutils: pypa/distutils#387
    Looks like that won't be solved any time soon, which wasn't obvious when the guide was written.

    It is not necessary to pass an /EXPORT flag to the linker; the PyMODINIT_FUNC & PyMODEXPORT_FUNC macros now contain the necessary declaration. Could you test if your workaround can be simplified to the following?

        def get_export_symbols(self, ext):
            return []

    But for the guide, I think a better workaround would be adding a dummy PyInit_ function to your extension. This function would only be there to satisfy the linker, it should never be called, so it can always fail. Something like this:

    // Workaround for https://github.com/pypa/distutils/issues/387
    PyMODINIT_FUNC
    PyInit_<modname>(void)
    {
        PyErr_SetString(PyExc_SystemError, "PyInit_* called for module with PyModExport_*");
        return NULL;
    }
  3. sbSteveK commented on Sep 24, 2026

    @sbSteveK
    Author

    Confirmed the simplified workaround.
    Tested on our Windows CI (windows-2022, MSVC 14.44, CPython 3.15.0rc2 free-threaded x64):

    With the export list suppressed, the extension links cleanly and PyModExport__awscrt appears in the DLL's export table via PyMODEXPORT_FUNC's dllexport alone, no /EXPORT flag needed. The wheel builds, imports, and passes our test suite.

    Change: awslabs/aws-crt-python@[commit] (PR awslabs/aws-crt-python#768).

    We went with the get_export_symbols() override rather than the dummy PyInit_ function since we already subclass build_ext for the abi3t wheel tag and .abi3t.so filename handling. The dummy-function approach is probably the better guide recommendation as it's build-tool-agnostic and keeps working for people who don't customize their build.

    I recommend you have the dummy set an exception rather than silently returning NULL so any unexpected PyInit_ call surfaces loudly.

    Thanks for the pointer to pypa/distutils#387,we've updated our comments to reference it as the root cause.

  4. added a commit that references this issue on Sep 29, 2026
  5. encukou commented on Sep 29, 2026

    @encukou
    Member

    Thanks.
    I'd still like to add this to the guide, to help others.

  6. added a commit that references this issue on Oct 2, 2026
  7. added a commit that references this issue on Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    OS-windowsdocsDocumentation in the Doc dir

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions