[python] Cython 코드가 포함 된 Python 패키지를 어떻게 구성해야합니까?

Cython 코드가 포함 된 Python 패키지를 만들고 싶습니다 . Cython 코드가 잘 작동합니다. 그러나 이제 나는 그것을 포장하는 가장 좋은 방법을 알고 싶습니다.

패키지 만 설치하려는 대부분의 사람들을 위해 .cCython이 생성 한 파일 을 포함 setup.py하고 모듈을 생성 하기 위해 컴파일 하도록 준비 하고 싶습니다 . 그러면 사용자는 패키지를 설치하기 위해 Cython을 설치할 필요가 없습니다.

그러나 패키지를 수정하려는 사람들을 위해 Cython .pyx파일도 제공 하고 어떻게 든 setup.pyCython을 사용하여 빌드 할 수도 있습니다 (그러면 사용자 Cython을 설치해야 함).

이 두 시나리오를 모두 충족하려면 패키지의 파일을 어떻게 구성해야합니까?

사이 썬 문서는 약간의 지침을 제공합니다 . 하지만 싱글을 만드는 방법은 나와 있지 않습니다.setup.py Cython의 유무에 관계없이 모두를 처리 .



답변

나는 파이썬 패키지 simplerandom( BitBucket repo- 편집 : 이제 github 에서 직접 수행했습니다 . ) 이것이 인기있는 패키지가 될 것이라고 기대하지는 않지만 Cython을 배울 수있는 좋은 기회였습니다).

이 방법은 (적어도 Cython 버전 0.14로) .pyx파일 을 빌드하는 Cython.Distutils.build_ext것이 항상 .c소스와 동일한 디렉토리에 파일 을 만드는 것처럼 보인다 는 사실에 의존합니다..pyx 파일 합니다.

다음은 setup.py필수 사항을 보여주는 축소 버전입니다 .

from distutils.core import setup
from distutils.extension import Extension

try:
    from Cython.Distutils import build_ext
except ImportError:
    use_cython = False
else:
    use_cython = True

cmdclass = {}
ext_modules = []

if use_cython:
    ext_modules += [
        Extension("mypackage.mycythonmodule", ["cython/mycythonmodule.pyx"]),
    ]
    cmdclass.update({'build_ext': build_ext})
else:
    ext_modules += [
        Extension("mypackage.mycythonmodule", ["cython/mycythonmodule.c"]),
    ]

setup(
    name='mypackage',
    ...
    cmdclass=cmdclass,
    ext_modules=ext_modules,
    ...
)

또한 소스 배포 (로 생성 된 소스 배포)에 포함 MANIFEST.in되도록 편집 했습니다 .mycythonmodule.cpython setup.py sdist

...
recursive-include cython *
...

mycythonmodule.c버전 관리 ‘트렁크'(또는 Mercurial의 경우 ‘기본값’)에 전념하지 않습니다 . 릴리스를 만들 때 소스 코드 배포를 위해 최신 상태로 존재 python setup.py build_ext하는지 확인하기 위해 첫 번째 작업을 수행해야합니다 mycythonmodule.c. 릴리스 브랜치를 만들고 C 파일을 브랜치에 커밋합니다. 그렇게하면 해당 릴리스와 함께 배포 된 C 파일의 기록 기록이 있습니다.


답변

Craig McQueen의 답변에 추가 : 재정의하는 방법은 아래를 참조하십시오. sdist Cython이 소스 배포를 만들기 전에 소스 파일을 자동으로 컴파일 명령을 .

이렇게하면 실수로 오래된 C소스를 배포 할 위험이 없습니다 . 또한 배포 프로세스를 제한적으로 제어 할 수있는 경우에도 도움이됩니다 (예 : 연속 통합에서 배포를 자동으로 생성하는 경우 등).

from distutils.command.sdist import sdist as _sdist

...

class sdist(_sdist):
    def run(self):
        # Make sure the compiled Cython files in the distribution are up-to-date
        from Cython.Build import cythonize
        cythonize(['cython/mycythonmodule.pyx'])
        _sdist.run(self)
cmdclass['sdist'] = sdist


답변

http://docs.cython.org/en/latest/src/userguide/source_files_and_compilation.html#distributing-cython-modules

사용자가 Cython을 사용할 필요없이 모듈을 설치할 수 있도록 생성 된 .c 파일과 Cython 소스를 배포하는 것이 좋습니다.

배포하는 버전에서 Cython 컴파일을 기본적으로 활성화하지 않는 것이 좋습니다. 사용자가 Cython을 설치 했더라도 모듈을 설치하는 데만 사용하고 싶지는 않을 것입니다. 또한 그가 가지고있는 버전이 당신이 사용한 것과 같지 않을 수도 있고 당신의 소스를 올바르게 컴파일하지 못할 수도 있습니다.

이는 단순히 함께 제공되는 setup.py 파일이 생성 된 .c 파일의 일반 distutils 파일 일 뿐이라는 것을 의미합니다.

from distutils.core import setup
from distutils.extension import Extension

setup(
    ext_modules = [Extension("example", ["example.c"])]
)

답변

가장 쉬운 방법은 둘 다 포함하고 c- 파일 만 사용하는 것입니까? .pyx 파일을 포함하는 것은 좋지만 어쨌든 .c 파일이 있으면 필요하지 않습니다. .pyx를 다시 컴파일하려는 사람들은 Pyrex를 설치하고 수동으로 수행 할 수 있습니다.

그렇지 않으면 먼저 C 파일을 빌드하는 distutils에 대한 사용자 정의 build_ext 명령이 필요합니다. Cython에는 이미 하나가 포함되어 있습니다.http://docs.cython.org/src/userguide/source_files_and_compilation.html

문서가하지 않는 것은 이것을 조건부로 만드는 방법을 말하는 것이지만

try:
     from Cython.distutils import build_ext
except ImportError:
     from distutils.command import build_ext

처리해야합니다.


답변

(Cython) 생성 된 .c 파일을 포함하는 것은 꽤 이상합니다. 특히 그것을 git에 포함시킬 때. setuptools_cython 을 사용하고 싶습니다. . Cython을 사용할 수없는 경우 Cython 환경이 내장 된 달걀을 빌드 한 다음 달걀을 사용하여 코드를 빌드합니다.

가능한 예 : https://github.com/douban/greenify/blob/master/setup.py


업데이트 (2017-01-05) :

이후 setuptools 18.0, 사용할 필요가 없습니다 setuptools_cython. 다음 은 .NET없이 Cython 프로젝트를 처음부터 빌드하는 예제 setuptools_cython입니다.


답변

이것은 빌드 내부에 중첩 된 디렉토리를 더 쉽게 포함 할 수 있도록 제가 작성한 설정 스크립트입니다. 패키지 내의 폴더에서 실행해야합니다.

다음과 같은 Givig 구조 :

__init__.py
setup.py
test.py
subdir/
      __init__.py
      anothertest.py

setup.py

from setuptools import setup, Extension
from Cython.Distutils import build_ext
# from os import path
ext_names = (
    'test',
    'subdir.anothertest',
)

cmdclass = {'build_ext': build_ext}
# for modules in main dir      
ext_modules = [
    Extension(
        ext,
        [ext + ".py"],
    )
    for ext in ext_names if ext.find('.') < 0]
# for modules in subdir ONLY ONE LEVEL DOWN!! 
# modify it if you need more !!!
ext_modules += [
    Extension(
        ext,
        ["/".join(ext.split('.')) + ".py"],
    )
    for ext in ext_names if ext.find('.') > 0]

setup(
    name='name',
    ext_modules=ext_modules,
    cmdclass=cmdclass,
    packages=["base", "base.subdir"],
)
#  Build --------------------------
#  python setup.py build_ext --inplace

행복한 컴파일;)


답변

내가 생각 해낸 간단한 해킹 :

from distutils.core import setup

try:
    from Cython.Build import cythonize
except ImportError:
    from pip import pip

    pip.main(['install', 'cython'])

    from Cython.Build import cythonize


setup(…)

가져올 수없는 경우 Cython을 설치하십시오. 아마도이 코드를 공유해서는 안되지만, 내 자신의 의존성을 위해서는 충분합니다.