背景
我为使用模块(https://luarocks.org/)管理软件包的系统编写了小型python软件包。对于那些不了解它的人,可以运行module load x
,然后运行一个小的脚本来修改各种环境变量以使软件“x”正常工作,然后可以使用module unload x
撤消该操作。
这种软件管理方法在科学计算中几乎是无处不在,并且在该 Realm 具有很大的值(value):您可以将古老的未维护软件与该软件可能会干扰的软件包一起运行,可以运行多个版本的软件,从而使自己的产品再现。准确地记录数据(您可以返回到旧版本),并且可以运行具有过时依赖关系的坦率地编写未更新的软件。
这些功能很棒,但是它们在python 2/3 split中造成了问题:
如果您想编写一个同时与 python 2和3一起使用的软件包并将其与需要python 2或3的软件一起使用,该怎么办?
使旧的python2依赖软件在这些大型系统上工作的方式是,您制作了python/2.7.x模块和python/3.5模块。当您要运行使用python 2的脚本时,请加载该模块等。
但是,我想编写一个可以在环境中的中使用的单个python软件包,因为无论使用哪种python解释器,我都希望该软件处于 Activity 状态。
从根本上讲,这非常简单:只需使用#!/usr/bin/env python
shebang行即可。那个有效。我写的所有软件都可以使用,所以没有问题。
题
问题是:我想使用setuptools将我的软件包分发给处于相同情况的其他科学家,而setup工具会破坏shebang线。
我不想就修改shebang线是否是一个好主意进行辩论,我相信这是因为它已经在同一状态下存在多年了。老实说,我不在乎,这对我不起作用。默认的setuptools安装会导致软件无法运行,因为当未加载python解释器的模块时,该python解释器不起作用,PYTHONPATH
完全是错误的。
如果我所有的用户都具有root访问权限,则可以使用data_files
选项将脚本复制到/usr/bin
,但这对兼容性不是一个好主意,而且我的用户也没有root访问权限,因此这是一个有争议的问题。
到目前为止,我尝试过的事情:
我尝试将sys.executable
文件中的/usr/bin/env python
设置为setup.py
,但这不起作用,因为那之后的爆炸是:#!"/usr/bin/env python"
,这显然不起作用。
我在以下问题中尝试了“不要触碰我的shebang类(class)想法”:Don't touch my shebang!(这是答案为0的最底答案)。那也不起作用,可能是因为它是为distutils而不是setuptools编写的。再加上这个问题已有6年历史了。
我还看了这些问题:
Setuptools entry_points/console_scripts have specific Python version in shebang
Changing console_script entry point interpreter for packaging
那里描述的方法不起作用,shebang线仍被更改。
使用内容创建一个setup.cfg
文件:
[build]
executable = /usr/bin/env python
也不改变shebang行的行为。
setuptools github页面上有一个 Unresolved 问题,讨论了类似的内容:
https://github.com/pypa/setuptools/issues/494
因此,我认为这不可能在本地完成,但是我想知道是否有解决方法?
最后,我不喜欢任何涉及要求用户修改安装标志的解决方案,例如与
-e
。无论如何,有没有修改此行为的方法,或者我可以使用另一个分发系统来代替?还是这太过分了,我只需要编写某种自定义安装脚本?
谢谢大家
更新资料
我想我对最初的问题还不够清楚,我希望用户能够做到的是:
如果有一种方法可以在不阻止shebang罪恶的情况下做到这一点,那就太好了。
我所有的代码都已经可以立即与python 2.7和python 3.3+兼容,主要是使脚本无论运行的python环境如何都可以运行。
最佳答案
在尝试编写自定义安装脚本时,我偶然发现了一种解决方法。
import os
from setuptools import setup
from setuptools.command.install import install
here = os.path.abspath(os.path.dirname(__file__))
# Generate a list of python scripts
scpts = []
scpt_dir = os.listdir(os.path.join(here, 'bin'))
for scpt in scpt_dir:
scpts.append(os.path.join(here, 'bin', scpt))
class ScriptInstaller(install):
"""Install scripts directly."""
def run(self):
"""Wrapper for parent run."""
super(ScriptInstaller, self).run()
setup(
cmdclass={'install': ScriptInstaller},
scripts=scpts,
...
)
这段代码并没有完全满足我的要求(只是在shebang行上),实际上只是将整个脚本复制到
~/.local/bin
,而不是将其包装在:__import__('pkg_resources').run_script()
此外,更重要的是,此方法使setuptools创建一个根模块目录以及一个egg-info目录,如下所示:
.local/lib/python3.5/site-packages/cluster
.local/lib/python3.5/site-packages/python_cluster-0.6.1-py3.5.egg-info
而不是一个鸡蛋,这是通常的行为:
.local/lib/python3.5/site-packages/python_cluster-0.6.1-py3.5.egg
据我所知,这是旧distutils的行为,这使我担心此安装将在某些系统上失败或具有其他意外行为(尽管如果我错了,请更正我,我真的不是这方面的专家) 。
但是,鉴于我的代码几乎将全部用在linux和OS X上,这还不是世界末日。我更担心这种行为很快就会消失。
我在setuptools github页面上对开放功能请求发表了评论:
https://github.com/pypa/setuptools/issues/494
理想的解决方案是,如果可以在setup.cfg中添加
executable=/usr/bin/env python
语句,希望可以很快重新实现。不过,此变通办法目前仍对我有效。谢谢大家