egu*_*aio 11 python distutils setuptools cython setup.py
我有一个名为mypackinside 的包,里面有一个模块mymod.py,而且__init__.py.出于某种原因,我不需要辩论,我需要打包这个模块(也不允许使用.py或.pyc文件).也就是说,它__init__.py是分布式压缩文件中允许的唯一源文件.
文件夹结构是:
.
?
??? mypack
? ??? __init__.py
? ??? mymod.py
??? setup.py
Run Code Online (Sandbox Code Playgroud)
我发现Cython能够通过转换.so库中的每个.py文件来实现这一点,该文件可以直接用python导入.
问题是:setup.py文件必须如何才能轻松打包和安装?
目标系统有一个virtualenv,其中必须使用允许轻松安装和卸载的任何方法安装软件包(easy_install,pip等都是受欢迎的).
我尝试了所有触手可及的东西.我阅读setuptools和distutils文档,计算器所有相关的问题,并试图与所有类型的命令(sdist,bdist,bdist_egg等),有很多setup.cfg和MANIFEST.in文件条目的组合.
我得到的最接近的是下面的安装文件,它将子类化bdist_egg命令以删除.pyc文件,但这会破坏安装.
如果覆盖了正确安装中包含的所有辅助文件(我需要pip freeze在venv中运行并查看mymod==0.0.1),那么在venv中
"手动"安装文件的解决方案也很好.
运行它:
python setup.py bdist_egg --exclude-source-files
Run Code Online (Sandbox Code Playgroud)
和(尝试)安装它
easy_install mymod-0.0.1-py2.7-linux-x86_64.egg
Run Code Online (Sandbox Code Playgroud)
您可能会注意到,目标是使用python 2.7的linux 64位.
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'],
)
Run Code Online (Sandbox Code Playgroud)
UPDATE.在@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 )
Run Code Online (Sandbox Code Playgroud)
关键是要设置packages=[],.需要覆盖build_ext类run方法才能将__init__.py文件放在方向盘内.
虽然打包作为一个轮子绝对是你想要的,但最初的问题是从包中排除.py源文件.这可以通过使用Cython来保护@Teyras的Python代码库来解决,但是他的解决方案使用了一个hack:它从调用中删除了packages参数setup().这可以防止build_py步骤运行,这实际上排除了.py文件,但它也排除了包含在包中的任何数据文件.(例如,我的包有一个名为VERSION的数据文件,其中包含软件包版本号.)更好的解决方案是使用仅复制数据文件的自定义命令替换build_py setup命令.
您还需要__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()
Run Code Online (Sandbox Code Playgroud)
并配置setup以覆盖原始build_py命令:
setup(
...
cmdclass={'build_py': CustomBuildPyCommand},
)
Run Code Online (Sandbox Code Playgroud)
不幸的是,关于设置的公认答案packages=[]是错误的,并且可能破坏很多东西,例如可以在此问题中看到。不要使用它。而不是从dist中排除所有软件包,您应该仅排除将被cythonized并编译为共享对象的python文件。
下面是一个工作示例;它使用我的食谱中的食谱从python bdist_egg或bdist_wheel中排除单个源文件。示例项目包含spam具有两个模块的程序包spam.eggs和spam.bacon,以及spam.fizz具有一个模块的子程序包spam.fizz.buzz:
root
??? setup.py
??? spam
??? __init__.py
??? bacon.py
??? eggs.py
??? fizz
??? __init__.py
??? buzz.py
Run Code Online (Sandbox Code Playgroud)
模块查询是在build_py命令中完成的,因此您需要使用自定义行为对其进行子类化。
如果要编译每个.py文件(包括__init__.pys),则覆盖build_py.build_packages方法已经足够,使其成为noop。由于build_packages不执行任何操作,因此根本不会.py收集任何文件,并且dist将仅包含cythonized扩展名:
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},
)
Run Code Online (Sandbox Code Playgroud)
如果只想编译选定的模块,而其余的都保持不变,那么您将需要更复杂的逻辑。在这种情况下,您需要覆盖模块查找。在下面的例子中,我仍然编译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},
)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5809 次 |
| 最近记录: |