当我在Windows上使用setup.py build_ext --inplace
命令编译任意一个__init__.py文件时,它具有无法解析的外部符号错误(即“
LINK:错误LNK2001:无法解析的外部符号PyInit___init__”)。
当地环境:
python3.7,
Cython 0.29.14,
window10 x64,
Microsoft Visual Studio 2017,
ctest / init.py
# cython: language_level=3
print('__init__')
setup.py
from distutils.core import setup
from Cython.Build import cythonize
def compile_code(name, filename):
setup(
name=name,
ext_modules=cythonize(filename),
)
if __name__ == '__main__':
compile_code('a', 'ctest/__init__.py')
终端打印的信息:
Compiling ctest/__init__.py because it changed.
[1/1] Cythonizing ctest/__init__.py
running build_ext
building 'ctest.__init__' extension
creating build
creating build\temp.win-amd64-3.7
creating build\temp.win-amd64-3.7\Release
creating build\temp.win-amd64-3.7\Release\ctest
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\bin\HostX86\x64\cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD -Id:\py37\include -Id:\py37\incl
ude "-IC:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include" "-IC:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\include\um" "-IC:\Pro
gram Files (x86)\Windows Kits\10\include\10.0.18362.0\ucrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\shared" "-IC:\Program Files (x86)\Windows Kits\10\includ
e\10.0.18362.0\um" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\winrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\cppwinrt" /Tcctest/__init__
.c /Fobuild\temp.win-amd64-3.7\Release\ctest/__init__.obj
__init__.c
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\bin\HostX86\x64\link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTU
AC:NO /LIBPATH:d:\py37\Libs /LIBPATH:D:\ENVS\cpytrantest\libs /LIBPATH:D:\ENVS\cpytrantest\PCbuild\amd64 "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC
\Tools\MSVC\14.16.27023\lib\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\lib\um\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.18362.0\ucrt\x6
4" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.18362.0\um\x64" /EXPORT:PyInit___init__ build\temp.win-amd64-3.7\Release\ctest/__init__.obj /OUT:C:\Users\76923\Deskto
p\cpythonrecord\ctest\__init__.cp37-win_amd64.pyd /IMPLIB:build\temp.win-amd64-3.7\Release\ctest\__init__.cp37-win_amd64.lib
LINK : error LNK2001: An unresolvable external symbol PyInit___init__
build\temp.win-amd64-3.7\Release\ctest\__init__.cp37-win_amd64.lib : fatal error LNK1120: An external command that cannot be parsed
error: command 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Tools\\MSVC\\14.16.27023\\bin\\HostX86\\x64\\link.exe' failed with exit status 1120
也许此行为可能被视为distutils
-package中的一个小错误(如@DavidW所指出的,存在一个未解决的问题:https
://bugs.python.org/issue35893 )。但是,它也表明,cythonize /
compiling__init__.py
并不是很流行,并且使用了一些未记录的实现细节,这些细节将来可能会改变,因此最好不要干预__init__.py
。
但是如果你必须…
显式导入包时,例如
import ctest
或隐含地,例如
import ctest.something
该FileFinder
会看到一个包,而不是一个模块,导入并会尝试加载
ctest/__init__.py
,而不是ctest.py
(这很可能是不存在的):
# Check if the module is the name of a directory (and thus a package).
if cache_module in cache:
base_path = _path_join(self.path, tail_module)
for suffix, loader_class in self._loaders:
init_filename = '__init__' + suffix
full_path = _path_join(base_path, init_filename)
if _path_isfile(full_path):
return self._get_spec(loader_class, fullname, full_path, [base_path], target)
用于suffix, loader_class
加载__init__.so
,__init__.py
并且__init__.pyc
按此顺序使用(另请参见此SO-
post)。这意味着,__init__.so
将加载而不是__init__.py
如果我们设法创建一个。
在__init__.py
执行时,该属性__name__
是程序包的名称,即ctest
您的情况,而不是您__init__
想象的那样。因此,在init函数的名称,Python的解释器将在加载时,分机呼叫__init__.so
是PyInit_ctest
在你的情况(而不是PyInit___init__
像一些人认为)。
上面说明了为什么所有这些都可以在Linux上直接使用。Windows呢?
加载程序只能使用来自so / dll的未隐藏符号。默认情况下,使用gcc构建的所有符号都是可见的,但Windows上的VisualStudio则不可见-
默认情况下,所有符号都是隐藏的(请参见此SO-post)。
但是,C扩展名的init函数必须是可见的(并且只有init函数),以便可以在加载程序的帮助下进行调用-
解决方案是PyInit_ctest
在链接时导出此符号(即),在您情况下,是/EXPORT:PyInit___init__
链接器的错误选项。
这个问题可以在distutils中找到,或者更精确地在build_ext
-class中找到:
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
可悲的ext.name
是__init__
在这里。
从这里开始,一种可能的解决方案很简单:覆盖get_export_symbols
,即将以下内容添加到您的setup.py
-file中(请阅读以获取更简单的版本):
...
from distutils.command.build_ext import build_ext
def get_export_symbols_fixed(self, ext):
names = ext.name.split('.')
if names[-1] != "__init__":
initfunc_name = "PyInit_" + names[-1]
else:
# take name of the package if it is an __init__-file
initfunc_name = "PyInit_" + names[-2]
if initfunc_name not in ext.export_symbols:
ext.export_symbols.append(initfunc_name)
return ext.export_symbols
# replace wrong version with the fixed:
build_ext.get_export_symbols = get_export_symbols_fixed
...
python setup.py build_ext -i
现在调用应该足够了(因为__init__.so
将被加载而不是__init__.py
)。
但是,正如@DawidW指出的,Cython使用macro
PyMODINIT_FUNC
,其定义为
#define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject*
与Py_EXPORTED_SYMBOL
被标记为出口在Windows可见/:
#define Py_EXPORTED_SYMBOL __declspec(dllexport)
因此,无需在命令行上将符号标记为可见。更糟糕的是,这是发出LNK4197警告的原因:
init 。obj:警告LNK4197:多次指定导出’PyInit_ctest’;使用第一规范
作为PyInit_test
被标记为__declspec(dllexport)
,并通过选项导出/EXPORT:
在同一时间。
/EXPORT:
-option将被distutils跳过,如果export_symbols
为空,我们甚至可以使用更简单的版本command.build_ext
:
...
from distutils.command.build_ext import build_ext
def get_export_symbols_fixed(self, ext):
pass # return [] also does the job!
# replace wrong version with the fixed:
build_ext.get_export_symbols = get_export_symbols_fixed
...
这甚至比第一个版本更好,因为它还修复了警告LNK4197!
问题内容: 我在理解项目中python文件的使用场景或设计目标时遇到了困难。 假设我有“模型”目录(称为包),其中包含以下文件 我发现了两种使用方法: 我有需要使用一个共同的定义,,。我可以用作所有* model.py类的基础/通用定义吗?这意味着我必须导入。 或者,应当具有自己导入的solrmodel.py,mongomodel.py,samodel.py的定义,并且它允许像这样的类或函数的轻松
问题内容: 我的软件包具有以下结构: 我不确定应如何正确写入文件。 的样子: 但是例如应该看起来如何?我的是: 什么时候应该使用? 问题答案: 很好-它有助于指导导入语句,而无需自动导入模块 http://docs.python.org/tutorial/modules.html#importing-from-a- package 使用和是多余的,仅需要 我认为在导入软件包中使用的最强大的理由之一
问题内容: 我正在使用Flask Framework 构建网站,其中有一个文件夹,其中有一些python文件和脚本(我想您将此文件夹称为模块吗?)。在 init .py文件中,我有一行话: 我现在想在此文件夹中的其他脚本中使用。通常,我会使用进行此操作,但这样做似乎不合适,更不用说pythonic了。此外,由于它在文件中,因此我认为应该以某种方式对整个文件夹/模块进行初始化。 有人知道如何使用该文
问题内容: 现在,我已经在Windows 7上成功安装了Cython,我尝试使用Cython编译一些Cython代码,但是gcc使我的生活变得艰难。 使用gcc编译代码时,会抛出数十个 对 -erros的 未定义引用 ,并且我很确定src是可用的(如安装教程所述,如果缺少此文件,则会抛出 对 -errors的 未定义引用 )。 奇怪的是,使用*或-script可以很好地工作,但是当仍然在模块上工作
init.py
init.py 指定导入该包时,仅导入 Client 类,代码如下: __all__ = ['Client'] from heatclient.v1.client import Client