有没有办法使用 SWIG C++ 创建一个 python 模块,它可以在 Python2 和 Python3 中导入

阿皮特

我正在编写一个 SWIG c++ 文件来生成一个 python 模块。我想让用户在 Python2 和 Python3 脚本中导入它。由于 SWIG 具有用于绑定 Python2 和 Python 3 的不同标志,我想知道有没有一种方法可以为两者编写一个通用模块。

柔印

让我们倒退一点,让 SWIG 本身远离问题开始。

从历史上看,有必要为 Python 解释器的每个(有时甚至是次要的)版本更改编译一个 Python 模块。这导致了PEP-384,它为 Python 3.2 以后的 Python C-API 的子集定义了一个稳定的 ABI。很明显,这实际上并不能满足您的要求,因为您对 2 vs 3 感兴趣。此外,SWIG 本身实际上并不生成PEP-384 兼容的代码。

为了进一步试验并了解更多关于正在发生的事情,我制作了以下 SWIG 文件:

%module test
%inline %{
  char *frobinate(const char *in) {
    static char buf[1024];
    snprintf(buf, 1024, "World of hello: %s", in);
    return buf;
  }
%}

如果我们尝试编译-DPy_LIMITED_API它失败:

swig3.0 -Wall -python test.i && gcc -Wall -Wextra -I/usr/include/python3.2 -shared -o _test.so test_wrap.c -DPy_LIMITED_API 2>&1|head
test_wrap.c: In function ‘SWIG_PyInstanceMethod_New’:
test_wrap.c:1114:3: warning: implicit declaration of function ‘PyInstanceMethod_New’ [-Wimplicit-function-declaration]
   return PyInstanceMethod_New(func);
   ^
test_wrap.c:1114:3: warning: return makes pointer from integer without a cast
test_wrap.c: In function ‘SWIG_Python_UnpackTuple’:
test_wrap.c:1315:5: warning: implicit declaration of function ‘PyTuple_GET_SIZE’ [-Wimplicit-function-declaration]
     Py_ssize_t l = PyTuple_GET_SIZE(args);
     ^
test_wrap.c:1327:2: warning: implicit declaration of function ‘PyTuple_GET_ITEM’ [-Wimplicit-function-declaration]

即没有人拿到那张票,至少在我正在测试的 SWIG 3.0.2 版本上没有。

那么,这让我们何去何从?好吧,关于 Python 3 支持SWIG 3.0 文档说了一些有趣的事情:

SWIG 能够支持 Python 3.0。SWIG 生成的包装器代码可以使用 Python 2.x 或 3.0 进行编译此外,通过将 -py3 命令行选项传递给 SWIG,可以生成具有一些 Python 3 特定功能的包装器代码(有关这些功能的详细信息,请参见下面的小节)。-py3 选项还禁用了 Python 3 的一些不兼容功能,例如 -classic。

我对那句话的理解是,如果你想生成可以用 2.x 和 3.x Python 头编译的源代码,你需要做的就是-py3在运行 SWIG 时省略参数。这似乎适用于我的测试,由 SWIG 生成的相同代码可以很好地编译所有:

$ gcc -Wall -Wextra -I/usr/include/python2.7/ -shared -o _test.so test_wrap.c 
$ gcc -Wall -Wextra -I/usr/include/python3.2 -shared -o _test.so test_wrap.c 
$ gcc -Wall -Wextra -I/usr/include/python3.4 -shared -o _test.so test_wrap.c

(请注意,3.4 确实会生成一些警告,但没有错误并且确实有效)

问题是任何给定版本的已编译 _test.so 之间没有互换性。尝试从一个版本导入另一个版本将失败,动态链接器会出现有关丢失或未定义符号的错误。所以我们仍然停留在最初的问题上,虽然我们可以为任何 Python 解释器编译我们的模块,但我们不能为所有版本编译它。

在我看来,解决这个问题的正确方法是使用distuitls并将您的模块安装到您想要在任何给定机器上支持的每个 Python 版本的搜索路径中。

也就是说,我们可以使用一种解决方法。本质上,我们希望为每个解释器构建多个版本的模块,并使用一些通用代码在各种实现之间切换。假设您没有使用 SWIG-builtin选项生成的代码进行构建,我们可以尝试在两个地方执行此操作:

  1. 在共享对象本身中
  2. 在一些 Python 代码中

后者实现起来要简单得多,所以让我们看一下。我通过使用以下 bash 脚本为我打算支持的每个 Python 版本构建一个共享对象做了一些事情:

#!/bin/bash

for v in 2.7 3.2 3.4
do
    d=$(echo $v|tr . _)
    mkdir -p $d
    touch $d/__init__.py
    gcc -Wall -Wextra -I/usr/include/python$v -shared -o $d/_test.so test_wrap.c
done

这会在以它们打算支持的 Python 版本命名的目录中构建 3 个版本的 _test.so。如果我们现在尝试导入 SWIG 生成的模块,它会抱怨,因为没有指示在哪里可以找到_test我们编译模块。所以我们需要解决这个问题。有以下三种方法:

  1. 修改 SWIG 生成的导入代码我没能用 SWIG 3.0.2 完成这项工作 - 也许它太旧了?
  2. 在第二次导入之前修改 Python 搜索路径。我们可以使用%pythonbegin

    %pythonbegin %{
    import os.path
    import sys
    sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '%d_%d' % (sys.version_info.major, sys.version_info.minor)))
    %}
    
  3. 编写一个 _test.py 存根,找到正确的存根,然后切换它们:

    from sys import version_info,modules
    from importlib import import_module
    real_mod = import_module('%d_%d.%s' % (version_info.major, version_info.minor, __name__))
    modules[__name__] = modules[real_mod.__name__] 
    

最后两个选项中的任何一个都有效,并import test在所有三个版本的 Python 中都取得了成功。但是,仅从源代码安装 3 次以上确实是作弊,而且要好得多。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Python3:有没有办法像python2一样使用telnetlib,而没有ascii编码和b前缀?

来自分类Dev

使用python2和python3创建一个virtualenv

来自分类Dev

在python3中导入模块

来自分类Dev

无法在 Python3 中导入 MySQLdb,但 Python2 可以工作

来自分类Dev

有没有办法以编程方式从python模块导入对象?

来自分类Dev

有没有办法动态导入Python对象?

来自分类Dev

有没有办法扩展swig / python中的所有类?

来自分类Dev

Boost - 在 Python 中导入 Qt 创建的 C++ Python 模块

来自分类Dev

有没有办法备份Python模块?

来自分类Dev

如何使一个对象同时具有Python2和Python3迭代器?

来自分类Dev

在python中导入模块

来自分类Dev

是否可以在python中导入一个模块,然后将其删除,但仍然可以在程序中使用它?

来自分类Dev

有没有办法组合使用python迭代多行的2个txt文件的内容?

来自分类Dev

在python中导入带有参数的模块

来自分类Dev

有没有办法对python装饰器中的参数和注释中的类型进行本地导入?

来自分类Dev

有没有办法使用python将一个大的json文件转换成csv格式

来自分类Dev

我可以使用函数在python中导入模块吗

来自分类Dev

有没有办法在python中“堆叠”一个列表?

来自分类Dev

无法在python3中导入pymysql模块

来自分类Dev

在 Python3 中导入模块时出错

来自分类Dev

有没有办法我可以返回数据并提供其他参数,python3 @click

来自分类Dev

Python 有没有办法返回一个没有前 n 个元素的列表?

来自分类Dev

在python中导入系统模块

来自分类Dev

从Python列表中导入模块

来自分类Dev

无法在python中导入模块

来自分类Dev

在openshift中导入python模块

来自分类Dev

在python中导入Vlc模块

来自分类Dev

无法在python中导入模块

来自分类Dev

在openshift中导入python模块

Related 相关文章

  1. 1

    Python3:有没有办法像python2一样使用telnetlib,而没有ascii编码和b前缀?

  2. 2

    使用python2和python3创建一个virtualenv

  3. 3

    在python3中导入模块

  4. 4

    无法在 Python3 中导入 MySQLdb,但 Python2 可以工作

  5. 5

    有没有办法以编程方式从python模块导入对象?

  6. 6

    有没有办法动态导入Python对象?

  7. 7

    有没有办法扩展swig / python中的所有类?

  8. 8

    Boost - 在 Python 中导入 Qt 创建的 C++ Python 模块

  9. 9

    有没有办法备份Python模块?

  10. 10

    如何使一个对象同时具有Python2和Python3迭代器?

  11. 11

    在python中导入模块

  12. 12

    是否可以在python中导入一个模块,然后将其删除,但仍然可以在程序中使用它?

  13. 13

    有没有办法组合使用python迭代多行的2个txt文件的内容?

  14. 14

    在python中导入带有参数的模块

  15. 15

    有没有办法对python装饰器中的参数和注释中的类型进行本地导入?

  16. 16

    有没有办法使用python将一个大的json文件转换成csv格式

  17. 17

    我可以使用函数在python中导入模块吗

  18. 18

    有没有办法在python中“堆叠”一个列表?

  19. 19

    无法在python3中导入pymysql模块

  20. 20

    在 Python3 中导入模块时出错

  21. 21

    有没有办法我可以返回数据并提供其他参数,python3 @click

  22. 22

    Python 有没有办法返回一个没有前 n 个元素的列表?

  23. 23

    在python中导入系统模块

  24. 24

    从Python列表中导入模块

  25. 25

    无法在python中导入模块

  26. 26

    在openshift中导入python模块

  27. 27

    在python中导入Vlc模块

  28. 28

    无法在python中导入模块

  29. 29

    在openshift中导入python模块

热门标签

归档