5

I created some Cython files, and import them in a Python file using,

import pyximport
pyximport.install()

import Cython_Mod1
import Cython_Mod2

When I run the py file, the C compiler (VC++14) generated the following errors

Cython_Mod1.obj : warning LNK4197: export 'PyInit_Cython_Mod1' specified multiple times; using first specification

for each Cythonmodule.

How to fix this and does it affect the performance or can be erroneous in the execution.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
daiyue
  • 7,196
  • 25
  • 82
  • 149

2 Answers2

7

LNK4197 is a warning, as such it shouldn't affect the compilation of your .c files, Cython probably just generates code that exports that function multiple times (for good reasons, I'd assume).

Since PyInit_<modname> is responsible for initializing the module; if your modules get initialized and imported correctly you have no issues. It won't affect the performance and/or result in erroneous execution.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
1

As @Jim's answer states it is only a (relatively benign) warning and can be ignored.

However, this answer presents more information and a fix.


When Python interpreter loads an extension/dll, module's init-function (e.g. PyInit_Cython_Mod1) must be visible/exported.

Cython uses macro PyMODINIT_FUNC to mark the init-function, which is defined as

#define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject*

with Py_EXPORTED_SYMBOL being marked as visible/exported on Windows:

#define Py_EXPORTED_SYMBOL __declspec(dllexport)

Thus, there is no need to export the symbol via linker options in the command line.

However, probably due to historical reasons, distutils plays safe and exports the init-symbol also via /EXPORT:-linker options, as can be seen here:

...
export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])]
...

export_symbols are calculated by get_export_symbols:

def get_export_symbols(self, ext):
    """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.
    """
    initfunc_name = "PyInit_" + ext.name.split('.')[-1]
    if initfunc_name not in ext.export_symbols:
        ext.export_symbols.append(initfunc_name)
    return ext.export_symbols

However, because we don't need any symbols being exported via linker-option, we could override this method as follows:

from distutils.command.build_ext import build_ext
def get_export_symbols_fixed(self, ext):
    return []

# replace wrong version with the fixed:
build_ext.get_export_symbols = get_export_symbols_fixed

import pyximport
pyximport.install()
...

No warning this time!

ead
  • 32,758
  • 6
  • 90
  • 153