mypack
という名前のパッケージがあり、その中にモジュールmymod.py
と__init__.py
があります。議論されていない何らかの理由で、このモジュールをコンパイルしてパッケージ化する必要があります(.pyまたは.pycファイルも許可されていません)。つまり、__init__.py
は、分散圧縮ファイルで許可されている唯一のソースファイルです。
フォルダ構造は次のとおりです。
.
│
├── mypack
│ ├── __init__.py
│ └── mymod.py
├── setup.py
Cythonは、Pythonで直接インポートできる.soライブラリ内の各.pyファイルを変換することでこれを実行できることがわかりました。
問題は、パッケージ化とインストールを簡単にするために、setup.py
ファイルをどのようにする必要があるかということです。
ターゲットシステムにはvirtualenvがあり、簡単にインストールおよびアンインストールできる方法でパッケージをインストールする必要があります(easy_install、pipなどはすべて歓迎されます)。
私は手の届くところにあるすべてを試しました。 setuptools
とdistutils
のドキュメント、すべてのstackoverflow関連の質問を読み、setup.cfgとMANIFESTの多くの組み合わせを使用して、あらゆる種類のコマンド(sdist、bdist、bdist_Eggなど)を試しました。ファイルエントリ内。
私が得た最も近いものは、.pycファイルも削除するためにbdist_Eggコマンドをサブクラス化する以下のセットアップファイルでしたが、それはインストールを壊しています。
適切なインストールに含まれるすべての補助ファイルがカバーされている場合は、venvにファイルを「手動で」インストールするソリューションも適しています(venvでpip freeze
を実行し、mymod==0.0.1
を参照する必要があります)。 )。
それを実行します:
python setup.py bdist_Egg --exclude-source-files
そして(しようと)それをインストールします
easy_install mymod-0.0.1-py2.7-linux-x86_64.Egg
お気づきかもしれませんが、ターゲットはLinux64ビットでpython 2.7です。
from Cython.Distutils import build_ext
from setuptools import setup, find_packages
from setuptools.extension import Extension
from setuptools.command import bdist_Egg
from setuptools.command.bdist_Egg import walk_Egg, log
import os
class my_bdist_Egg(bdist_Egg.bdist_Egg):
def zap_pyfiles(self):
log.info("Removing .py files from temporary directory")
for base, dirs, files in walk_Egg(self.bdist_dir):
for name in files:
if not name.endswith('__init__.py'):
if name.endswith('.py') or name.endswith('.pyc'):
# original 'if' only has name.endswith('.py')
path = os.path.join(base, name)
log.info("Deleting %s",path)
os.unlink(path)
ext_modules=[
Extension("mypack.mymod", ["mypack/mymod.py"]),
]
setup(
name = 'mypack',
cmdclass = {'build_ext': build_ext,
'bdist_Egg': my_bdist_Egg },
ext_modules = ext_modules,
version='0.0.1',
description='This is mypack compiled lib',
author='Myself',
packages=['mypack'],
)
更新。 @Teyrasの回答に続いて、回答で要求されたとおりにホイールを作成することができました。 setup.py
ファイルの内容は次のとおりです。
import os
import shutil
from setuptools.extension import Extension
from setuptools import setup
from Cython.Build import cythonize
from Cython.Distutils import build_ext
class MyBuildExt(build_ext):
def run(self):
build_ext.run(self)
build_dir = os.path.realpath(self.build_lib)
root_dir = os.path.dirname(os.path.realpath(__file__))
target_dir = build_dir if not self.inplace else root_dir
self.copy_file('mypack/__init__.py', root_dir, target_dir)
def copy_file(self, path, source_dir, destination_dir):
if os.path.exists(os.path.join(source_dir, path)):
shutil.copyfile(os.path.join(source_dir, path),
os.path.join(destination_dir, path))
setup(
name = 'mypack',
cmdclass = {'build_ext': MyBuildExt},
ext_modules = cythonize([Extension("mypack.*", ["mypack/*.py"])]),
version='0.0.1',
description='This is mypack compiled lib',
author='Myself',
packages=[],
include_package_data=True )
重要な点は、packages=[],
を設定することでした。ホイール内にbuild_ext
ファイルを取得するには、__init__.py
クラスrun
メソッドの上書きが必要でした。
残念ながら、_packages=[]
_を設定して受け入れられた回答は間違っており、多くのものを壊す可能性があります。 この質問 で見られます。使用しないでください。 distからすべてのパッケージを除外するのではなく、cythonizedされて共有オブジェクトにコンパイルされるpythonファイルのみを除外する必要があります。
以下は実際の例です。 私のレシピ 質問から 単一のソースファイルをpython bdist_Eggまたはbdist_wheel から除外します。サンプルプロジェクトには、2つのモジュールを持つパッケージspam
が含まれています。 _spam.eggs
_と_spam.bacon
_、および1つのモジュールを含むサブパッケージ_spam.fizz
_ _spam.fizz.buzz
_:
_root
├── setup.py
└── spam
├── __init__.py
├── bacon.py
├── eggs.py
└── fizz
├── __init__.py
└── buzz.py
_
モジュールルックアップは_build_py
_コマンドで実行されるため、カスタム動作でサブクラス化する必要があります。
すべての_.py
_ファイル(___init__.py
_ sを含む)をコンパイルしようとしている場合は、_build_py.build_packages
_メソッドをオーバーライドするだけで十分です。 _build_packages
_は何もしないため、_.py
_ファイルはまったく収集されず、distには暗号化された拡張子のみが含まれます。
_import fnmatch
from setuptools import find_packages, setup, Extension
from setuptools.command.build_py import build_py as build_py_orig
from Cython.Build import cythonize
extensions = [
# example of extensions with regex
Extension('spam.*', ['spam/*.py']),
# example of extension with single source file
Extension('spam.fizz.buzz', ['spam/fizz/buzz.py']),
]
class build_py(build_py_orig):
def build_packages(self):
pass
setup(
name='...',
version='...',
packages=find_packages(),
ext_modules=cythonize(extensions),
cmdclass={'build_py': build_py},
)
_
選択したモジュールのみをコンパイルし、残りはそのままにしておきたい場合は、もう少し複雑なロジックが必要になります。この場合、モジュールルックアップをオーバーライドする必要があります。以下の例では、_spam.bacon
_、_spam.eggs
_、および_spam.fizz.buzz
_を共有オブジェクトにコンパイルしますが、___init__.py
_ファイルはそのままにして、ソースモジュールとして含まれるようにします。
_import fnmatch
from setuptools import find_packages, setup, Extension
from setuptools.command.build_py import build_py as build_py_orig
from Cython.Build import cythonize
extensions = [
Extension('spam.*', ['spam/*.py']),
Extension('spam.fizz.buzz', ['spam/fizz/buzz.py']),
]
cython_excludes = ['**/__init__.py']
def not_cythonized(tup):
(package, module, filepath) = tup
return any(
fnmatch.fnmatchcase(filepath, pat=pattern) for pattern in cython_excludes
) or not any(
fnmatch.fnmatchcase(filepath, pat=pattern)
for ext in extensions
for pattern in ext.sources
)
class build_py(build_py_orig):
def find_modules(self):
modules = super().find_modules()
return list(filter(not_cythonized, modules))
def find_package_modules(self, package, package_dir):
modules = super().find_package_modules(package, package_dir)
return list(filter(not_cythonized, modules))
setup(
name='...',
version='...',
packages=find_packages(),
ext_modules=cythonize(extensions, exclude=cython_excludes),
cmdclass={'build_py': build_py},
)
_
ホイールとしてパッケージ化することは間違いなくあなたが望むものですが、元の質問はパッケージから。pyソースファイルを除外することについてでした。これは Cythonを使用してPython codebase を@Teyrasで保護しますが、彼のソリューションはハックを使用します:を削除します) setup()
の呼び出しからのpackages引数。これにより、build_pyステップが実行されなくなります。 、確かに、。pyファイルを除外しますが、パッケージに含めたいデータファイルも除外します(たとえば、私のパッケージには、というデータファイルがあります。パッケージのバージョン番号を含むVERSION。)より良い解決策は、build_pyセットアップコマンドを、データファイルのみをコピーするカスタムコマンドに置き換えることです。
上記のように、__init__.py
ファイルも必要です。したがって、カスタムbuild_pyコマンドで__init_.py
ファイルを作成する必要があります。コンパイルされた__init__.so
はパッケージのインポート時に実行されるため、必要なのは空の__init__.py
ファイルだけで、ディレクトリがモジュールであることをPythonインポートしても大丈夫です。
カスタムbuild_pyクラスは次のようになります。
import os
from setuptools.command.build_py import build_py
class CustomBuildPyCommand(build_py):
def run(self):
# package data files but not .py files
build_py.build_package_data(self)
# create empty __init__.py in target dirs
for pdir in self.packages:
open(os.path.join(self.build_lib, pdir, '__init__.py'), 'a').close()
そして、元のbuild_pyコマンドをオーバーライドするようにsetupを構成します。
setup(
...
cmdclass={'build_py': CustomBuildPyCommand},
)
(fish2000で提案されているように)ホイール形式を使用することをお勧めします。次に、setup.py
で、packages
引数を[]
に設定します。 Cython拡張機能は引き続きビルドされ、結果の.soファイルは結果のwheelパッケージに含まれます。
__init__.py
がホイールに含まれていない場合は、Cythonから出荷されたbuild_ext
クラスのrun
メソッドをオーバーライドして、ファイルをソースツリーからビルドフォルダー(パス)にコピーできます。 self.build_lib
にあります)。
これはまさに一種の問題でした Pythonホイールフォーマット – PEP 427で説明 –対処するために開発されました。
ホイールはPython卵(さまざまな理由で問題があった/問題があった)の代わりになります– pip
でサポートされています 、アーキテクチャを含めることができます-特定のプライベートバイナリ(ここに そのような配置の一例 )があり、これらの種類のものに利害関係を持つPythonコミュニティによって一般的に受け入れられています。
これが1つですsetup.py
aforelinkedPython on Wheelsの記事の抜粋で、バイナリ分布を設定する方法を示しています。
import os
from setuptools import setup
from setuptools.dist import Distribution
class BinaryDistribution(Distribution):
def is_pure(self):
return False
setup(
...,
include_package_data=True,
distclass=BinaryDistribution,
)
…使用している古い(しかし、おそらくまだ正規にサポートされている)setuptools
クラスのleuで。概説したように、配布目的でWheelsを作成するのは非常に簡単です。経験から思い出すと、wheel
モジュールのビルドプロセスはvirtualenv
をある程度認識しているか、非常に簡単に使用できます。他の中で。
いずれにせよ、setuptools
EggベースのAPIをホイールベースのツールと交換することで、深刻な苦痛を軽減できると思います。