2017-05-20 9 views
1

저는 파이썬 모듈을 생성하기 위해 SWIG C++ 파일을 작성하고 있습니다. 사용자가 Python2 및 Python3 스크립트에서 가져올 수 있도록하고 싶습니다. SWIG는 Python2와 Python 3을 묶는 데 다른 플래그를 가지고 있기 때문에 두 모듈 모두를위한 일반 모듈을 작성할 수있는 방법이 있는지 궁금합니다.Python2와 Python3 모두에서 가져올 수있는 SWIG C++을 사용하여 파이썬 모듈을 만드는 방법이 있습니까

답변

1

조금만 되감 으셔서 SWIG를 처음부터 꺼내십시오.

역사적으로 파이썬 인터프리터의 모든 (때로는 심지어 사소한) 버전 변경을 위해 파이썬 모듈을 컴파일해야했습니다. 이것은 PEP-384으로 이어졌으며, Python 3.2 이후의 Python C-API의 하위 집합에 대한 안정적인 ABI를 정의했습니다. 당신이 2 대 3에 관심이 있기 때문에 그렇게 분명히 당신의 요청에 실제로 작동하지 않습니다. 게다가 SWIG는 실제로 PEP-384 compatible, 코드를 생성하지 않습니다.

더 실험하고 난 다음 꿀꺽 꿀꺽 파일했습니다에 무슨 일이 일어나고 있는지에 대해 좀 더 보려면 : 우리가 실패 -DPy_LIMITED_API 이것을 컴파일하려고하면

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

을 :

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의 버전에는이 티켓을받지 못했습니다.

그렇다면 어디에서 우리를 떠날까요? 그럼 SWIG 3.0 documentation on Python 3 support은 흥미로운 것을 말합니다 :

SWIG는 Python 3.0을 지원할 수 있습니다. SWIG에서 생성 된 래퍼 코드는 Python 2.x 또는 3.0으로 컴파일 할 수 있습니다. 더 나아가, -py3 명령 행 옵션을 SWIG에 전달함으로써, 파이썬 3의 특정 기능을 포함하는 래퍼 코드를 생성 할 수 있습니다 (이러한 기능에 대한 자세한 내용은 하위 섹션 참조). -py3 옵션은 -classic과 같이 Python 3의 일부 호환되지 않는 기능을 비활성화합니다. 그 진술의

내 독서는 모두 2.x 및 3.x 파이썬 헤더 당신이해야 할 모든 컴파일 할 수 있습니다 소스 코드를 생성 할 경우 꿀꺽 꿀꺽을 실행할 때 -py3 인수를 생략 것입니다.

$ 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간에 호환성이 없다는 것입니다. 하나의 버전에서 다른 버전으로 버전을 가져 오려고하면 동적 링커에서 누락되거나 정의되지 않은 기호에 대한 오류가 발생하여 실패합니다. 따라서 우리는 여전히 초기 문제에 직면 해 있습니다. 모든 파이썬 인터프리터에 대해 모듈을 컴파일 할 수 있지만 모든 버전에서 컴파일 할 수는 없습니다.

제 생각에는 올바른 방법으로 distuitls을 사용하고 특정 컴퓨터에서 지원하려는 Python의 각 버전에 대한 검색 경로에 모듈을 설치하기 만하면됩니다.

그렇다면 우리가 사용할 수있는 해결 방법이 있습니다. 본질적으로 우리는 각 인터프리터에 대해 모듈의 여러 버전을 빌드하고 다양한 구현간에 전환하기 위해 일반적인 코드를 사용하려고합니다.당신이 꿀꺽 꿀꺽의 -builtin 옵션에서 생성 된 코드가있는 건물하지 않는 것으로 가정하면 우리가 시도하고이 할 수있는 두 곳이 있습니다 : 일부 파이썬 코드에서 공유 객체 자체

  • 에서

    나중에 달성하기가 훨씬 간단하므로 그 부분을 살펴 보겠습니다.

    #!/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 
    

    이 그들이있어 파이썬 버전의 이름을 딴 디렉토리에, _test.so의 3 버전을 빌드 : 나는 지원하기위한 파이썬의 각 버전에 대한 공유 객체를 구축하기 위해 다음과 같은 bash는 스크립트를 사용하여 뭔가를했다 지원하기위한 것입니다. 우리가 SWIG 생성 모듈을 가져 오려고하면 지금 컴파일 한 모듈 _test을 어디에서 찾을 수 있는지 알려주지 않으므로 불평 할 것입니다. 그래서 우리는 그것을 고쳐야합니다. 이를 수행하는 세 가지 방법이 있습니다.

    1. 생성 된 SWIG를 수정하여 import code을 생성합니다. SWIG 3.0.2에서이 작업을 할 수 없었습니다. 아마도 너무 오래 되었습니까?
    2. 두 번째 가져 오기 전에 파이썬 검색 경로를 수정하십시오. 우리는 %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이 성공했습니다. 하지만 소스 코드를 3 번 ​​이상 설치하는 것이 정말 쉽고 훨씬 더 나은 방법입니다.

  • +0

    그래서 인터페이스 파일에 "#if PY_MAJOR_VERSION <3"을 포함시키고 다른 파이썬 버전에 대해 별도로 래퍼 파일을 작성해야합니다. 출력 파일을 컴파일 한 후에 소프트웨어와 함께 번들로 제공되므로 출력 파일을 변경할 수 없습니다. – Arpit

    +0

    필요한 모든 ifdef가 자동으로 생성되며 -py3을 사용하지 않는다면 코드는 2와 3 모두에 대해 작동합니다. 일단 파이썬 버전 당 한 번 빌드해야합니다. – Flexo