背景

我为使用模块(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

无论如何,有没有修改此行为的方法,或者我可以使用另一个分发系统来代替?还是这太过分了,我只需要编写某种自定义安装脚本?

谢谢大家

更新资料

我想我对最初的问题还不够清楚,我希望用户能够做到的是:
  • 在python2和python3中都安装软件包(模块将进入lib/pythonx/site-lib。
  • 不管运行哪个python环境,都能够运行脚本。

  • 如果有一种方法可以在不阻止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语句,希望可以很快重新实现。

    不过,此变通办法目前仍对我有效。谢谢大家

    08-24 16:26
    查看更多