覆盖python setuptools中的shebang mangling

Mik*_*cre 6 python linux setuptools

背景

我为使用模块(https://luarocks.org/)管理软件包的系统编写了小型python 软件包。对于那些不了解它的人,可以运行module load x并运行一个小的脚本来修改各种环境变量以使软件“ x”正常工作,然后可以使用撤消该操作module unload x

这种软件管理方法在科学计算中几乎无处不在,并且在该领域具有很大的价值:您可以将古老的未维护软件与该软件可能会干扰的软件包一起运行,可以运行多个版本的软件,从而使自己的产品再现。准确地记录数据(您可以返回到旧版本),并且可以运行具有过时依赖关系的坦率地编写未更新的软件。

这些功能很棒,但是它们在python 2/3 split中造成了问题:

如果您想编写一个同时适用于python 2和3 的软件包并将其与需要 python 2或3的软件一起使用怎么办?

使旧的python2依赖软件在这些大型系统上工作的方式是,您制作了python / 2.7.x模块和python / 3.5模块。当您要运行使用python 2的脚本时,请加载该模块等。

但是,我想编写一个可以在两种环境下都可以使用的python软件包,因为无论使用哪种python解释器,我都希望该软件处于活动状态。

从根本上讲,这非常容易:只需使用#!/usr/bin/env pythonshebang线就可以了。这样可行。我编写了所有软件都可以使用,所以没有问题。

问题是:我想使用setuptools将我的软件包分发给处于相同情况的其他科学家,而setup工具会破坏shebang线。

我不想就修改shebang线是否是一个好主意展开辩论,我相信这是因为它已经在同一状态下存在多年了。老实说,我不在乎,这对我不起作用。默认的setuptools安装会导致软件无法运行,因为当未加载python解释器的模块时,该python解释器将无法运行,这PYTHONPATH是完全错误的。

如果我所有的用户都具有root用户访问权限,则可以使用该data_files选项将脚本复制到/usr/bin,但这对兼容性不是一个好主意,而且我的用户也没有root用户访问权限,因此这是一个有争议的问题。

到目前为止,我尝试过的事情:

我尝试设置sys.executable/usr/bin/env pythonsetup.py文件,但不起作用,因为那时家当是:#!"/usr/bin/env python",这显然是行不通的。

我在以下问题中尝试了“不要触碰我的shebang类”想法:不要触碰我的shebang!(这是最低的答案,票数为0)。那也不起作用,可能是因为它是为distutils而不是setuptools编写的。再加上这个问题已有6年历史了。

我还看了这些问题:

Setuptools entry_points / console_scripts在shebang中具有特定的Python版本

更改用于包装的console_script入口点解释器

那里描述的方法不起作用,shebang线仍被更改。

创建一个setup.cfg包含以下内容的文件:

[build]
executable = /usr/bin/env python
Run Code Online (Sandbox Code Playgroud)

也不会改变shebang行的行为。

setuptools github页面上有一个未解决的问题,讨论了类似的内容:

https://github.com/pypa/setuptools/issues/494

因此,我认为这不可能在本地完成,但是我想知道是否有解决方法?

最后,我不喜欢任何涉及要求用户修改安装标志的解决方案,例如使用-e

无论如何,是否可以修改此行为,或者可以使用其他发行系统代替?还是这太过分了,我只需要编写某种自定义安装脚本即可?

谢谢大家


更新资料

我想我对最初的问题还不够清楚,我希望用户能够做到的是:

  • 将软件包安装在python2和python3中(模块将进入lib / pythonx / site-lib。
  • 不管运行哪个python环境,都能够运行脚本

如果有一种方法可以在不阻止shebang干扰的情况下做到这一点,那就太好了。

我所有的代码已经与python 2.7和python 3.3+兼容,即装即用,主要目的是使脚本无论运行的python环境如何都可以运行。

Mik*_*cre 2

我在尝试编写自定义安装脚本时无意中发现了一种解决方法。

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,
    ...
)
Run Code Online (Sandbox Code Playgroud)

这段代码并没有完全达到我想要的效果(只改变 shebang 行),它实际上只是将整个脚本复制到~/.local/bin,而不是将其包装在::

__import__('pkg_resources').run_script()
Run Code Online (Sandbox Code Playgroud)

此外,更令人担忧的是,此方法使 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
Run Code Online (Sandbox Code Playgroud)

而不是一个鸡蛋,这是通常的行为::

.local/lib/python3.5/site-packages/python_cluster-0.6.1-py3.5.egg
Run Code Online (Sandbox Code Playgroud)

据我所知,这是旧 distutils 的行为,这让我担心此安装会在某些系统上失败或出现其他意外行为(尽管如果我错了,请纠正我,我真的不是这方面的专家) 。

然而,考虑到我的代码几乎完全在 Linux 和 OS X 上使用,这并不是世界末日。我更担心这种行为很快就会消失。

我在 setuptools github 页面上发布了对开放功能请求的评论:

https://github.com/pypa/setuptools/issues/494

理想的解决方案是,如果我可以executable=/usr/bin/env python向 setup.cfg 添加一条语句,希望很快就能重新实现。

不过,这个解决方法目前对我有用。谢谢大家。