Skip to content

PyMODINIT_FUNC does not apply __declspec(dllexport) on Windows #141671

Description

@encukou

The macro is documented as:

Declare an extension module initialization function. This macro:

  • specifies the PyObject* return type,
  • adds any special linkage declarations required by the platform, and
  • for C++, declares the function as extern "C".

Specifically, the special linkage declarations should be __attribute__ ((visibility ("default"))) on GCC/clang, and __declspec(dllexport) on Windows.
However, the definition assumes we're building CPython itself, and only adds a linkage declaration with Py_ENABLE_SHARED -- that is, when Python itself is built with a shared library.

According to MSVC docs:

There are four methods for exporting a definition, listed in recommended order of use:

  1. __declspec(dllexport) in the source code
  2. An EXPORTS statement in a .def file
  3. An /EXPORT specification in a LINK command
  4. A comment directive in the source code, of the form #pragma comment(linker, "/export: definition ").

Setuptools, and historically distutils, does (2) -- and so does, presumably, any other build system for native extensions on Windows.
However, this breaks for modules that define the PEP-793 PyModExport hook.

It would be great to switch to the preferred (1), and then tell Setuptools to avoid /EXPORT for 3.15+.

Linked PRs

Activity

  1. self-assigned this
    on Nov 17, 2025
  2. added a commit that references this issue on Nov 17, 2025
  3. encukou commented on Nov 18, 2025

    @encukou
    MemberAuthor

    I filed a setuptools issue: pypa/setuptools#5118

  4. AraHaan commented on Dec 29, 2025

    @AraHaan
    Contributor

    I personally think this would be safe to be backported to 3.12, 3.13, 3.14 as adding __declspec(dllexport) to PyMODINIT_FUNC does not break anything.

    Also then the setuptools patch would also apply to those versions of python as well when they upgrade the package.

    This is because currently what I do to work around this was to

    #undef PyMODINIT_FUNC
    #if defined(__cplusplus)
    #define PyMODINIT_FUNC extern "C" Py_EXPORTED_SYMBOL PyObject*
    #else /* __cplusplus */
    #define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject*
    #endif /* __cplusplus */

    in my C extension code which I feel could have been avoided entirely.

  5. added a commit that references this issue on Jan 7, 2026
  6. encukou commented on Jan 7, 2026

    @encukou
    MemberAuthor

    I'd rather let this change have the full alpha/beta period before it hits production. It's old, platform-specific, widely used, and tied to build systems.

  7. rgommers commented on Jan 13, 2026

    @rgommers
    Contributor

    Setuptools, and historically distutils, does (2) -- and so does, presumably, any other build system for native extensions on Windows.

    For clarity: I think you meant that setuptools/distutils does (3), not (2). And other build systems/backends do (2).

  8. added a commit that references this issue on Feb 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions